Java Wait错误用法

  • 先来看看一段代码:
new Thread(() -> {
           synchronized (ReleaseLockDemo.class) {
               System.out.printf("线程[%s]进入1号n", Thread.currentThread().getName());
               try {
//                    Thread.sleep(1000);
                    Thread.currentThread().wait();

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.printf("线程【%s】退出1号n", Thread.currentThread().getName());
            }
        }).start();

        new Thread(() -> {
            synchronized (ReleaseLockDemo.class) {
                System.out.printf("线程[%s] 进入2号n", Thread.currentThread().getName());

                System.out.printf("线程【%s】退出2号n", Thread.currentThread().getName());
            }
        }).start();

运行结果

  • 这段代码的结果是:
线程[Thread-0]进入1号
Exception in thread "Thread-0" 线程[Thread-1] 进入2号
线程【Thread-1】退出2号
java.lang.IllegalMonitorStateException
	at java.lang.Object.wait(Native Method)
	at java.lang.Object.wait(Object.java:502)
	at com.concurrency.ReleaseLockDemo.lambda$main$0(ReleaseLockDemo.java:17)
	at java.lang.Thread.run(Thread.java:748)

实例疑问

为什么会是这样的结果呢?按道理来说, 应该是下面这个结果才对呀。它为什么会在1号线程等待的时候,2号线程运行了呢,它不应该是要等待1号线线程中的锁释放了才能运行的吗?又为什么会报两个错呢?

线程[Thread-0]进入1号
线程【Thread-0】退出1号
线程[Thread-1] 进入2号
线程【Thread-1】退出2号

那我们先总结下问题:

  1. 为什么会在1号线程等待的时候,2号线程运行了
  2. 为什么会报Exception in thread "Thread-0" 错
  3. 为什么会报java.lang.IllegalMonitorStateException 错

那么带着问题我们来分析下这段代码。更多教程请访问码农之家  

实例分析

  • 为什么会在1号线程等待的时候,2号线程运行了?
    • 就目前的代码而言,我们的锁对象都是ReleaseLockDemo.class,在两个同步代码块中,用同一个锁,一个代码块运行了,而另一个要运行的话,只有前面的锁释放了后面的代码块才能正常运行。可是在前面的1号线程只运行到了一半就直接运行2号线程了,中间只做了一个wait操作,难道wait操作会引发锁的释放吗?
    • 这是因为在调用wait()方法时,monitor被释放了,并且进入休眠状态,然后唤醒其他的线程(可能一个或多个),此时其他线程是可以重新获取到该monitor object的。
  • 那么我们来看看这个报错IllegalMonitorStateException, 这是一个monitor报错
    • 要想了解这个报错,首先得了解下什么是monitor
      • monitor就是监视器,操作系统在面对 进程/线程间同步的时候,所支持的一些同步原语,一般的monitor实现模式是编程语言在语法上提供语法糖,而如何实现monitor机制,则属于编译器的工作,Java就是这么干的。
      • monitor的重要特点是,同一时刻,只有一个进程/线程能进入monitor中定义的临界区,这使得monitor能够达到互斥的效果。但仅仅有呼哧的作用是不够的,无法进入monitor临界区的进程/线程,他们应该被阻塞,并且在必要的时候会被唤醒。
      • 在Java中的具体实现就是Synchronized;Synchronized关键字在使用时,往往需要指定一个对象与之关联(如:synchronized(this)),而这个对象就是monitor object。
      • 一个线程通过调用某个对象的wait()方法释放该对象的monitor并进入休眠状态,知道其他线程获取来被该线程释放的monitor并调用该对象的notify()或者notifyAll()后再次竞争获取该对象的monitor。这也解释了前面的问题,为什么2号线程会运行。
      • 只有拥有该对象monitor的线程才可以调用该对象的notify()和notifyAll()方法;如果没有该对象monitor的线程调用了该对象的notify()或者notifyAll()方法将会抛出java.lang.IllegalMonitorStateException
    • 调用wait()方法必须要用拥有该对象monitor的线程才可以正常调用,而我们的代码中synchronzed所锁住的对象是ReleaseLockDemo.class。因此,执行该程序后报java.lang.IllegalMonitorStateException错误。
  • 至于为什么会报Exception in thread "Thread-0" 错,这个错跟后面的IllegalMonitorStateException是同一个错,只是因为当时线程休眠了,没有执行完而已。

如果换成拥有当前类的对象呢

  • 既然要调用wait()方法那么如果我们把monitor object换成当前类的对象会不会就不会报错了呢?

上代码

public static void main(String[] args) {
        WaitMethodTest wmt = new WaitMethodTest();
        wmt.run1();
        wmt.run2();
    }

    private  void run2() {
        new Thread(() -> {
            synchronized (this) {
                System.out.printf("线程[%s] 进入2号n", Thread.currentThread().getName());

                System.out.printf("线程【%s】退出2号n", Thread.currentThread().getName());
            }
        }).start();
    }

    private  void run1() {
        new Thread(() -> {
            synchronized (this) {
                System.out.printf("线程[%s]进入1号n", Thread.currentThread().getName());
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.printf("线程【%s】退出1号n", Thread.currentThread().getName());
            }
        }).start();
    }

运行结果:

  线程Thread-0进入1号
  线程Thread-1 进入2号
  线程【Thread-1】退出2号

结果分析

  • 结果确实没有报错了,但是它好像也没有停止运行
  • 这是因为当2号线程运行完成后并没有唤醒1号线程,1号线程还在睡眠状态,所以整个程序没有停止运行。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值