为什么线程在持有锁而不是在获取锁之前测试条件/属性?

时间:2017-10-08 23:59:40

标签: java multithreading synchronization

来自Herlihy的多处理编程艺术:

  

假设一个线程   想要等到某个房产持有。 线程测试属性   持有锁。如果属性不成立,则线程调用await()   释放锁并睡觉,直到它被另一个线程唤醒。

1 Condition condition = mutex.newCondition();
2 ...
3 mutex.lock()
4 try {
5     while (!property) { // not happy
6         condition.await(); // wait for property
7     } catch (InterruptedException e) {
8         ... // application-dependent response
9     }
10    ... // happy: property must hold
11 }
     

图8.2如何使用Condition对象。

为什么线程在测试属性之前调用mutex.lock()(如果属性不持有,线程调用await())而不是之后?

感谢。

2 个答案:

答案 0 :(得分:1)

如果您将其更改为:

while (!property) {
   mutex.lock();
   condition.await();
   mutex.unlock();

最大的问题是如果属性值在行

之间发生变化
while (!property) {

和行

   mutex.lock();

然后,当它已经处于通过状态时,你会等待属性改变。

如果您认为只能在锁定互斥锁时更改属性,那么对于您问题中的示例,您无法调用

   condition.await();

当财产处于通过状态时。

要详细说明这个假设,问题中的示例代码通常会以某种方式设置属性值。这可能与

有关
void setProperty(boolean newproperty) {
    mutex.lock();
    property = newproperty;
    condition.signalAll();
    mutex.unlock();
}

如果没有第二部分,你永远不能保证只打电话

condition.await();

虽然属性是假的。

答案 1 :(得分:0)

获取锁定的一件事是创建一个内存屏障,以便不使用可能已过时的缓存值检查条件。

同时获取锁定可确保检查的值不会被其他某个线程同时更改。否则,线程可以测试条件并继续获取锁定,然后在获取锁定时找到更改的条件。该线程最终需要检查所持有的锁。

线程想要确定锁定对象的状态,然后修改该状态,同时保证由于另一个线程的干扰,该状态不会从其下面改变。这意味着它必须在检查和行动中保持锁定。线程在检查失败后等待(释放锁直到发出信号),然后在下次检查之前获取锁。一旦检查成功,它就会保持锁定,直到它可以完成对锁定对象的操作。

只有在检查成功时(如添加到有界缓冲区),对锁定对象执行的操作才有意义。或者操作可能涉及多个更改,其中所有更改都需要一起应用(如果锁定对象具有不是为并发访问而设计的数据结构,如ArrayList,那么数据结构需要多个步骤来添加元素,并且干扰可以破坏数据结构)。