为什么Object.wait(),Object.notify(),Object.notifyAll()必须在同步块中执行呢?

背景介绍

在Java中,我们使用wait()nofify()notifyAll()来实现线程间通信。一个线程在测试条件不满足后进入等待状态。在经典的生产者-消费者问题中,生产者线程因缓存区满而等待,消费者线程在消费了缓存区的一个元素后通知生产者线程。

调用notify()notifyAll()方法来通知一个或多个线程一个条件已经改变了。一旦通知线程退出同步方法或同步块,所有等待的线程会争抢它们等待对象上的对象锁。获取锁的线程会从等待状态返回并继续执行

代码示例

一般而言,使用Object的wait/notify进行现场通信代码如下图示,wait()notify()都在同步块中执行.
这里写图片描述
wait()notify()不在同步块中执行,会出现什么情况呢?

/**
 * @author pfjia
 * @since 2018/3/6 15:40
 */
public class IllegalWaitDemo {
    public static void main(String[] args) {
        IllegalWaitDemo demo=new IllegalWaitDemo();
        try {
            demo.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

执行上述代码后,结果如下,抛出java.lang.IllegalMonitorStateException:

Exception in thread "main" java.lang.IllegalMonitorStateException
    at java.base/java.lang.Object.wait(Native Method)
    at java.base/java.lang.Object.wait(Object.java:516)
    at pfjia.IllegalWaitDemo.main(IllegalWaitDemo.java:11)

但是为什么会抛出java.lang.IllegalMonitorStateException呢?

竞态条件思路

试想在不加锁时,一个如下的操作顺序

         生产者
       while (!someCondition){ (1)
           lock.wait(); (4)
       }

        消费者
       satisfyCondition(); (2)
       lock.notify(); (3)

由于while(!someCondition){lock.wait()}是一个先判断后执行的语句,因此一定存在竞态条件,当上述代码以标识的顺序执行时,由于在!someCondition为true后才执行satisfyCondition()满足该condition,因此将导致即使condition被满足了,lock.wait()还是会在lock.notify()之后执行,就相当于丢失了一次唤醒.如果只有一个消费者线程,则生产者将永远得不到唤醒.

PV操作思路

我是将

   synchronized (lock){
       while (!someCondition){ (1)
           lock.wait(); (4)
       }
   }

看做P操作.

   synchronized (lock){
       satisfyCondition(); (2)
       lock.notify(); (3)
   }

看做V操作,而PV的资源含义则由someConditionsatisfyCondition()确定.
由于PV操作必须是原子操作,而若wait()notify()不在同步块中,就无法保证原子性,所以wait()notify()必须在同步块中执行.

参考

猜你喜欢

转载自blog.csdn.net/jpf254/article/details/79459560