背景介绍
在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的资源含义则由someCondition和satisfyCondition()确定.
由于PV操作必须是原子操作,而若wait()和notify()不在同步块中,就无法保证原子性,所以wait()和notify()必须在同步块中执行.
本文详细解释了Java中线程间通信的原理,包括wait()和notify()的正确使用方式及为何必须在同步块内调用。通过示例代码展示了非法调用可能导致的问题,并从竞态条件和PV操作的角度深入探讨其原因。
3157

被折叠的 条评论
为什么被折叠?



