为什么wait()方法要放在同步块中?

为了避免Lost Wake-Up问题,那什么是Lost Wake-Up问题呢?我们通过两个线程,一个模拟消费者线程,另一个模拟生产者线程来说明问题。我们简化生产者线程的任务是count加一,而后唤醒消费者;消费者的任务是将count减一,在减到0的时候陷入睡眠。我们通过一组伪代码看一下:

生产者伪代码:

count+1;
notify();

消费者伪代码:

while(count <= 0){
  wait()
}
count--

熟悉多线程的朋友一眼就能够看出来,这里面有问题。什么问题呢?生产者是两个步骤:count+1和notify();消费者也是两个步骤:检查count值;wait 或者count-1;万一这些步骤混杂在一起呢?比如说,初始的时候count等于0,这个时候消费者检查count的值,发现count小于等于0的条件成立;就在这个时候,发生了上下文切换,生产者进来了,噼噼啪啪一顿操作,把两个步骤都执行完了,也就是发出了通知,准备唤醒一个线程。这个时候消费者刚决定睡觉,还没睡呢,所以这个通知就会被丢掉。紧接着,消费者就睡过去了。如下图:

为什么wait()方法要放在同步块中?_第1张图片

这就是所谓的lost wake up问题。

那么怎么解决这个问题呢?

让消费者和生产者竞争一把锁,竞争到了的,才能够修改count的值。

生产者的代码:

tryLock();
count+1;
notify();
releaseLock();

消费者的代码: 

tryLock();
while(count <= 0 ){
  wait();
}
count-1;
releaseLock;

这样改毫无卵用,依旧会出现lost wake up问题,而且和无锁的表现是一样的。 

终极答案:

所以,我们可以总结到,为了避免出现这种lost wake up问题,我们的要把代码放进去的同步块中。Java强制我们的wait()/notify()调用必须要在一个同步块中,避免这种lost wake up问题。不仅仅是这两个方法,包括java.util.concurrent.locks.Condition的await()/signal()也必须要在同步块中。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(多线程)