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

举报
xindoo 发表于 2022/04/13 23:33:28 2022/04/13
【摘要】   前两天去面试,被问到了一个线程同步的问题,两个线程依次输出1……100,一个线程只输出奇数,一个只输出偶数。之前工作中没写过线程同步的代码,只知道使用object的wait()和notify()方法可...

  前两天去面试,被问到了一个线程同步的问题,两个线程依次输出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();
    }
}

文章来源: xindoo.blog.csdn.net,作者:xindoo,版权归原作者所有,如需转载,请联系作者。

原文链接:xindoo.blog.csdn.net/article/details/79987734

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。