Java线程间同步(诡异的IllegalMonitorStateException )

  前两天去面试,被问到了一个线程同步的问题,两个线程依次输出1……100,一个线程只输出奇数,一个只输出偶数。之前工作中没写过线程同步的代码,只知道使用object的wait()和notify()方法可以实现线程同步,之前也看过线程池实现的代码,用的也是wait()和notify()。 面试过程中没写出来,于是想回来学习下多线程的同步,然后就有了今天这诡异的事。
 
  思路很简单,创建两个线程threadEven和threadOdd分别来输出偶数和奇数,用一个Integer cnt来做数据同步,每个线程执行的时候先锁住cnt,然后输出cnt并把cnt+=1,然后通知另一个线程来执行并把本线程wait()挂起,于是有了下面的代码

public class ThreadA {
    private Integer cnt = new Integer(0);
    class ThreadEven extends Thread {
        @Override
        public void run() {
            while (true) {
                synchronized (cnt) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("threadEven " + cnt);
                    cnt++;
                    cnt.notify();
                    try {
                        cnt.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (cnt > 100) {
                        return;
                    }
                }
            }
        }
    }
    class ThreadOdd extends Thread {
        @Override
        public void run() {
            while (true) {
                synchronized (cnt) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("threadOdd  " + cnt);
                    cnt++;
                    cnt.notify();
                    try {
                        cnt.wait();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    if (cnt > 100) {
                        return;
                    }
                }
            }
        }
    }
    public static void main(String[] args)  {
        ThreadA test = new ThreadA();
        ThreadEven threadEven = test.new ThreadEven();
        ThreadOdd threadOdd = test.new ThreadOdd();
        threadEven.setName("threadEven");
        threadOdd.setName("threadOdd");
        threadEven.start();
        threadOdd.start();
    }
}

  代码看起来很完美,但运行后直接给我抛java.lang.IllegalMonitorStateException,上网查下这个exception,有三种情况下会出现这种Exception。
1. 当前线程不含有当前对象的锁资源的时候,调用obj.wait()方法。
2. 当前线程不含有当前对象的锁资源的时候,调用obj.notify()方法。
3. 当前线程不含有当前对象的锁资源的时候,调用obj.notifyAll()方法。

  代码中很明显我先对cnt做了同步,所以当前线程在执行中肯定是有cnt的锁的,那为什么我调cnt.notify();cnt.wait();的时候还会抛Exception? 上网查了好多资料后终于找到了问题。。

Integer是个不变类,任何对它的修改都会生成一个新的对象,同样的不变类还有String , Boolean, Double 。

  所以上面代码的问题就很明显了,我用synchronized锁住的cnt和执行cnt+=1后的cnt就不是同一个对象,而我代码中也没有获取cnt+=1后的cnt的锁,所以在执行 cnt.notify();cnt.wait();的时候会抛Exception。所以解决方法也很简单,把synchronized锁住的对象换成一个不变的就行,任何不变对象都可以,这里我用了concurrent.atomic.AtomicInteger,所以最终可正常运行的代码如下。

import java.util.concurrent.atomic.AtomicInteger;

public class ThreadA {
    private AtomicInteger cnt = new AtomicInteger();

    class ThreadEven extends Thread {
        @Override
        public void run() {
            while (true) {
                synchronized (cnt) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("threadEven " + cnt);
                    cnt.addAndGet(1);
                    cnt.notify();
                    try {
                        cnt.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (cnt.get()> 100) {
                        return;
                    }
                }
            }
        }
    }
    class ThreadOdd extends Thread {
        @Override
        public void run() {
            while (true) {
                synchronized (cnt) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("threadOdd  " + cnt);
                    cnt.addAndGet(1);
                    cnt.notify();
                    try {
                        cnt.wait();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    if (cnt.get() > 100) {
                        return;
                    }
                }
            }
        }
    }
    public static void main(String[] args)  {
        ThreadA test = new ThreadA();
        ThreadEven threadEven = test.new ThreadEven();
        ThreadOdd threadOdd = test.new ThreadOdd();
        threadEven.start();
        threadOdd.start();
    }
}

打赏

You May Also Like

About the Author: xindoo

Leave a Reply

Your email address will not be published.