前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java Wait错误用法

Java Wait错误用法

原创
作者头像
用户7046206
修改2020-03-23 11:08:34
1.1K0
修改2020-03-23 11:08:34
举报
文章被收录于专栏:一只小菜鸟一只小菜鸟

Java Wait 错误用法

实例观察

  • 先来看看一段代码:
代码语言:java
复制
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();

运行结果

  • 这段代码的结果是:
代码语言:java
复制
线程[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号线线程中的锁释放了才能运行的吗?又为什么会报两个错呢?

代码语言:java
复制
线程[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换成当前类的对象会不会就不会报错了呢?

上代码

代码语言:java
复制
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();
    }

运行结果:

代码语言:java
复制
  线程Thread-0进入1号
  线程Thread-1 进入2号
  线程【Thread-1】退出2号

结果分析

  • 结果确实没有报错了,但是它好像也没有停止运行
  • 这是因为当2号线程运行完成后并没有唤醒1号线程,1号线程还在睡眠状态,所以整个程序没有停止运行。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Java Wait 错误用法
    • 实例观察
      • 运行结果
        • 实例疑问
          • 实例分析
            • 如果换成拥有当前类的对象呢
              • 上代码
              • 运行结果:
              • 结果分析
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档