Java 为什么wait()必须始终处于同步块中
我们都知道,为了调用,这个调用必须放在synchronized块中,否则就会抛出一个。但是做出此限制的原因是什么?我知道Java 为什么wait()必须始终处于同步块中,java,multithreading,concurrency,wait,Java,Multithreading,Concurrency,Wait,我们都知道,为了调用,这个调用必须放在synchronized块中,否则就会抛出一个。但是做出此限制的原因是什么?我知道wait()会释放监视器,但是为什么我们需要通过同步特定块来显式获取监视器,然后通过调用wait()来释放监视器 如果可以在同步块外部调用wait(),保留其语义-挂起调用线程,那么潜在的损害是什么?只有在同时存在notify()时,才有意义,因此线程之间的通信总是相关的,这需要同步才能正常工作。有人可能会说这应该是隐含的,但这并没有真正的帮助,原因如下: 从语义上讲,您永远不
wait()
会释放监视器,但是为什么我们需要通过同步特定块来显式获取监视器,然后通过调用wait()
来释放监视器
如果可以在同步块外部调用wait()
,保留其语义-挂起调用线程,那么潜在的损害是什么?只有在同时存在notify()
时,才有意义,因此线程之间的通信总是相关的,这需要同步才能正常工作。有人可能会说这应该是隐含的,但这并没有真正的帮助,原因如下:
从语义上讲,您永远不会只是wait()
。你需要满足一些条件,如果不满足,你就等它满足。所以你真正做的是
if(!condition){
wait();
}
但是该条件是由一个单独的线程设置的,因此为了使其正常工作,您需要同步
还有一些问题,仅仅因为你的线程停止等待并不意味着你所寻找的条件是正确的:
- 您可以得到虚假的唤醒(意味着线程可以在没有收到通知的情况下从等待中唤醒),或者
- 可以设置条件,但第三个线程在等待的线程唤醒(并重新获取监视器)时再次使条件为false
synchronized(lock){
while(!condition){
lock.wait();
}
}
更好的是,不要弄乱同步原语,而是使用java.util.concurrent
包中提供的抽象
如果可以在同步块外部调用wait()
,保留其语义-挂起调用线程,那么潜在的损害是什么?
让我们用一个具体示例来说明如果可以在同步块之外调用wait()
,我们会遇到什么问题
假设我们要实现一个阻塞队列(我知道,API中已经有一个了:)
第一次尝试(没有同步)可以看到下面的内容
class BlockingQueue {
Queue<String> buffer = new LinkedList<String>();
public void give(String data) {
buffer.add(data);
notify(); // Since someone may be waiting in take!
}
public String take() throws InterruptedException {
while (buffer.isEmpty()) // don't use "if" due to spurious wakeups.
wait();
return buffer.remove();
}
}
类阻塞队列{
队列缓冲区=新的LinkedList();
public void give(字符串数据){
buffer.add(数据);
notify();//因为可能有人在等待!
}
公共字符串take()引发InterruptedException{
while(buffer.isEmpty())//由于虚假唤醒,请不要使用“if”。
等待();
return buffer.remove();
}
}
这就是可能发生的情况:
take()
,并看到缓冲区.isEmpty()
wait()
之前,生产者线程出现并调用完整的give()
,即buffer.add(数据);通知()代码>
wait()
(并错过刚才调用的notify()
)give()
,这是因为消费者线程从未唤醒,我们有一个死锁synchronized
确保在isEmpty
和wait
之间从未调用notify
无需详细说明:此同步问题是普遍存在的。正如MichaelBorgwardt指出的,wait/notify完全是关于线程之间的通信,因此您总是会遇到类似于上面描述的竞争条件。这就是为什么强制执行“仅在同步内等待”规则的原因
《圣经》中的一段话很好地概括了这一点: 您需要绝对保证服务员和通知者同意谓词的状态。服务员在谓词进入睡眠前的某个时间点检查谓词的状态,但其正确性取决于谓词进入睡眠时是否为true。这两个事件之间存在一段时间的漏洞,可能会破坏程序 生产者和消费者需要达成一致的谓词在上面的示例
buffer.isEmpty()
中。通过确保在synchronized
块中执行等待和通知来解决该协议
这篇文章在这里被改写为一篇文章:直接来自java oracle教程: 当线程调用d.wait时,它必须拥有d.wait的内在锁- 否则将抛出一个错误。在同步进程内调用wait 方法是获取内在锁的简单方法
@滚球是对的。调用
wait()
,这样线程就可以等待某种情况发生。当调用wait()
时,线程将被迫放弃其锁。要放弃某些东西,你首先需要拥有它。线程首先需要拥有锁。 因此需要在
synchronized
方法/块内调用它
是的,如果您没有检查synchronized
method/block中的条件,我同意上述所有关于潜在损害/不一致的回答。然而,正如@shrini1000所指出的,仅仅在synchronized块中调用wait()
,并不能避免这种不一致性的发生
这基本上与硬件架构有关(即RAM和缓存) 如果您不将
synchronized
与wait()
或notify()
一起使用,则另一个线程可能会进入相同的blo
class A {
private Object X;
makeChangeOnX(){
while (! x.getCondition()){
wait();
}
// Do the change
}
setConditionToTrue(){
x.condition = true;
notifyAll();
}
setConditionToFalse(){
x.condition = false;
notifyAll();
}
bool getCondition(){
return x.condition;
}
}
boolean wasNotified = false;
while(!wasNotified) {
wait();
}
synchronized(monitor) {
boolean wasNotified = false;
while(!wasNotified) {
wait();
}
}
class A {
int a = 0;
//something......
public void add() {
synchronization(this) {
//this is your monitoring object and thread has to wait to gain lock on **this**
}
}