我对此表示怀疑,在Java语言中,我们需要保持锁定,然后再等待满足某些条件。
例如,int java monitor lock:
synchronized(lock){
System.out.println("before lock ...");
lock.wait();
System.out.println("after lock ...");
}
或来自cucurrent utils。
Lock lock = new ReentrantLock();
Condition cond = lock.newCondition();
lock.lock();
try{
System.out.println("before condition ...");
cond.await();
System.out.println("after condition ...");
}catch(Exception e){
e.printStackTrace();
}finally{
lock.unlock();
}
那么,为什么我们不能等待,而不能保持锁定?
如果只是因为Java,其他语言的工作方式不同?
我希望您能在设计之后解释原因,但不仅仅是JAVA-SPEC定义。
答案 0 :(得分:12)
想象一下,你有一些线程可能需要等待的东西。也许你有一个队列,一个线程需要等到队列中的某些东西,以便它可以处理它。队列必须是线程安全的,因此必须通过锁保护。您可以编写以下代码:
哎呀,这不会奏效。我们保持对队列的锁定,那么另一个线程如何在其上放置一些东西?让我们再试一次:
存在条件变量来解决这个确切的问题。他们有一个原子"解锁和等待"关闭此窗口的操作。
所以等待必须持有锁,否则将无法确保您不等待已经发生的事情。您必须持有锁以防止另一个线程与您的等待竞争。
答案 1 :(得分:8)
那么,我们还在等什么呢?我们正在等待条件成为现实。另一个线程将使条件成立,然后通知等待的线程。
在进入等待之前,我们必须检查条件是否为假;此检查和等待必须是 atomic ,即在同一个锁下。否则,如果我们在条件已经为真的情况下进入等待,我们可能永远不会被唤醒。
因此必要在调用wait()
之前已经获取了锁
synchronized(lock)
{
if(!condition)
lock.wait();
如果wait()
自动且无声地获取锁定,则批次的错误将无法检测到。
从wait()
唤醒后,我们必须再次检查条件 - 无法保证条件必须在此处变为真(因为很多原因 - 虚假唤醒;超时,中断,多个服务员,多个条件)< / p>
synchronized(lock)
{
if(!condition)
lock.wait();
if(!condition) // check again
...
通常,如果条件仍为假,我们将再次等待。因此典型的模式是
while(!condition)
lock.wait();
但也有一些我们不想再等的情况。
裸露的等待/通知是否有合理的用例?
synchronized(lock){ lock.wait(); }
不确定;一个应用程序可以用裸等待/通知组成,具有明确定义的行为;可以认为这是理想的行为;这是该行为的最佳实现。
但是,这不是典型的使用模式,并且没有理由在API设计中考虑它。
答案 2 :(得分:1)
请参阅Condition的文档。
条件类似于对象的等待池或等待集,它取代了对象监视器方法(wait,notify和notifyAll)的使用。条件允许一个线程暂停执行(“等待”),直到另一个线程通知某个状态条件现在可能为真。 Condition实例本质上绑定到一个锁,就像Object监视器方法需要共享对象的锁等待或通知一样。因此,在对条件调用await()之前,线程必须已锁定用于生成条件的Lock对象。调用await()方法时,将释放与条件关联的锁。
答案 3 :(得分:1)
如果线程只是在等待信号继续进行,那么还有其他机制可以做到这一点。据推测,有一些状态受锁的保护,线程正在等待操作并满足某些条件。为了正确保护该状态,线程应该在等待条件之前和之后具有锁定,因此需要获取锁定是有意义的。
答案 4 :(得分:0)
合理的答案
这是JVM的事情。对象x
具有:
一个Entry Set
:尝试synchronized(x)
一个Waiting Set
:称为x.wait()
调用x.wait()
时,JVM将当前线程添加到Waiting Set
中;当您调用x.notify()
/ x.notifyAll()
时,JVM从Waiting Set
中删除一个/所有元素。
多个线程可以调用x.wait()
/ x.notify()
/ x.notifyAll()
来修改Waiting Set
。为了确保Waiting Set
线程的安全性,JVM一次只能接受一个线程的一项操作。
答案 5 :(得分:-1)
简单的答案是因为否则您将获得在Object.wait javadoc中指定的IllegalMonitorStateException。在内部,Java中的同步使用底层操作系统机制。所以它不仅仅是Java。