详解Java程序并发的Wait

网友投稿 202 2023-07-30


详解Java程序并发的Wait

Wait-Notify场景

典型的Wait-Notify场景一般与以下两个内容相关:

1. 状态变量(State Variable)

当线程需要wait的时候,总是因为一些条件得不到满足导致的。例如往队列里填充数据,当队列元素已经满时,线程就需要wait停止运行。当队列元素有空缺时,再继续自己的执行。

2. 条件断言(Condition Predicate)

当线程确定是否进入wait或者是从notify醒来的时候是否继续往下执行,大部分都要测试状态条件是否满足。例如,往队列里添加元素,队列已满,于是阻塞当前线程,当有其他线程从队列里取走了元素,就通知在等待的线程“队列有剩余空间,可以往里添加元素了”。这时,等待添加元素的进程就会被唤醒,然后判断一下当前队列是否真的有剩余空间,如果真的有剩余空间,就将元素添加进去,如果没有,则继续阻塞等待下次唤醒。

3. 条件队列(Condition Queue)

每个对象都有一个内置的条件队列,当一个线程在该对象锁上调用wait函数的时候,就会将该线程加入到该对象的条件队列中。

注意

wait与notify是java同步机制中的重要组成部分。结合与synchronized关键字使用,可以建立很多优秀的同步模型,例如生产者-消费者模型。但是在使用wait()、notify()、notifyAll()函数的时候,需要特别注意以下几点:

wait()、notify()、notifyAll()方法不属于Thread类,而是属于Object基础类,也就是说每个对象都有wait()、notify()、notifyAll()的功能。因为每个对象都有锁,锁是每个对象的基础,因此操作锁的方法也是最基础的。

    调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj){...} 代码段内。

    调用obj.wait()后,线程A就释放了obj的锁,否则线程B无法获得obj锁,也就无法在synchronized(obj){...} 代码段内唤醒线程A。

    当obj.wait()方法返回后,线程A需要再次获得obj锁,才能继续执行。

    如果线程A1,A2,A3都在obj.wait(),则线程B调用obj.notify()只能唤醒线程A1,A2,A3中的一个(具体哪一个由JVM决定)。

    如果线程B调用obj.notifyAll()则能全部唤醒等待的线程A1,A2,A3,但是等待的线程要继续执行obj.wait()的下一条语句,必须获得obj锁。因此,线程A1,A2,A3只有一个有机会获得锁继续执行,例如A1,其余的需要等待A1释放obj锁之后才能继续执行。

    当线程B调用obj.notify()或者obj.notifyAll()的时候,线程B正持有obj锁,因此,线程A1,A2,A3虽被唤醒,但是仍无法获得obj锁。直到线程B退出synchronized代码块,释放obj锁后,线程kMemRsFmcA1,A2,A3中的一个才有机会获得对象锁并得以继续执行。

示例代码

线程的wait操作的典型代码结构如下:

public void test() throws InterruptedException {

synchronized(obj) {

while (! contidition) {

obj.wait();

}

}

}

为什么obj.wait()操作必须位于循环中呢?有以下几个主要原因:

1. 一个对象锁可能用于保护多个状态变量,当它们都需要wait-notify操作时,如果不将wait放到while循环中就会有问题。例如,某对象锁obj保护两种状态变量a和b,当a的条件断言不成立时发生了wait操作,当b的条件断言不成立时也发生了wait操作,两个线程被加入到obj对应的条件队列中。现在若改变状态变量a的某操作发生,在obj上调用了notifyAll操作,则obj对应的条件队列里的所有线程均被唤醒,之前等待a的一个或几个线程去判断a的条件断言可能成立了,但http://是b对于的条件断言肯定仍不成立,而此时等待b的线程也被唤醒了,所以需要循环判断b的条件断言是否满足,如果不满足,则继续wait。

2. 多个线程wait的同一状态的条件断言。例如,向队列添加元素的场景,当前队列是满的,多个线程想往里面添加元素,于是都wait了。此时,另一个线程从队列里取出了一个元素,调用了notifyAll操作,唤醒了所有线程,但是只有一个线程能够往队列里添加一个元素,其他的仍需要等待。

3. 虚假唤醒。在没有被通知、中断或超时的情况下,线程自动苏醒了。虽然这种情况在实践中很少发生,但是通过循环等待可以杜绝这一情况的发生。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:使用Java代码来比较Android客户端版本号
下一篇:Java并发编程中的生产者与消费者模型简述
相关文章

 发表评论

暂时没有评论,来抢沙发吧~