是设置布尔值所必需的关键部分吗?

时间:2016-04-21 07:51:45

标签: java synchronization

假设我们有n个线程访问这个函数,我听说即使布尔值只是有点翻转,这个过程也不是原子的。在这个函数中,opening = true是否需要包含在同步中? opening是该班级的成员。

boolean opening = false;

public void open() {

    synchronized (this) {
        while (numCarsOnBridge != 0 || opening || closing) {
            // Wait if cars on bridge, opening or closing
            try {
                // Wait until all cars have cleared the bridge, until bridge opening
                wait();
            } catch (InterruptedException e) {}
        }
        // By now, no cars will be under the bridge.
    }

    if (!opening) {
        opening = true; // Do we need to wrap this in a synchronize?
        // notifyAll(); // Maybe need to notify everyone that it is opening
        try {
            sleep(60); // pauses the current thread calling this
        } catch (InterruptedException e) {}

        synchronized (this) {
            drawBridgeClosed = false; // drawBridge is opened
            opening = false;
            notifyAll(); // Only notify all when state has fully changed
        }
    }

}

3 个答案:

答案 0 :(得分:1)

是的,需要同步对布尔值的并发修改,使用AtomicBoolean或同步结构。

此外,您无需在此拥有监控器即可调用notifyAll():

if (!opening) {
    opening = true; // Do we need to wrap this in a synchronize?
    // notifyAll(); // Maybe need to notify everyone that it is opening 

所以你已经有2个理由将它包装在synchronized块中。

答案 1 :(得分:1)

实际上opening = true 原子的 - 它只是不会生成所谓的内存障碍

您应该opening(以及此closing} volatile

volatile boolean opening = false;

这会在每次opening更改时强制执行内存屏障,从而确保刷新opening变量的任何缓存。

答案 2 :(得分:1)

关键部分是必要的。为了解释这一点,我们需要考虑原子性。具体来说,我们需要对标志(opening)的读写操作是单个原子操作(原子意义,它们发生在一个不可分割的步骤中)。

考虑这个简化的例子;

if (flag) //read
{
      flag = false; //write
      foo(); //arbitrary operation
}

在这个例子中,读取发生,然后写入发生之后,任何事情都可能发生在两者之间(例如,线程#2出现并看到true之前,线程#1将值设置为{{ 1}},在这种情况下foo()将被调用两次。

要解决此问题,我们需要确保读取和写入都在一个步骤中完成。我们可以通过将两者放在同一个同步块中来实现这一点;

false

由于同步块中的所有内容都被认为是一个非常大的原子操作,因此读/写也是原子的,并且实现了线程安全性(因此每次将标志设置为true时,foo只会被一个线程调用一次)。

AtomicBoolean(在另一个答案中提到)也可以在这种情况下使用,因为这个类提供了在同一步骤中读写的操作。例如;

synchronized(monitor)
{ 
   if (flag) //read
   {
      flag= false; //write
      foo(); //some arbitrary operation
   }   
}

作为旁注," getAndSet"操作在功能上等同于这样的东西;

static AtomicBoolean flag = new AtomicBoolean(false);

public void run()
{
   if (flag.getAndSet(false)) //get and then set the flag
   {
      //the flag is now set to 'false'
      //and we will enter this section if it was originally 'true'
      foo();
   }
}

然而,它以高度优化的方式执行此操作,因此通常比使用synchronized关键字快得多。