有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

假设我们有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) 个答案

  1. # 1 楼答案

    实际上opening = true是原子的——它只是不产生所谓的记忆屏障

    你应该做opening(和closing关于这个问题)volatile

    volatile boolean opening = false;
    

    这将在每次opening更改时强制设置内存屏障,从而确保opening变量的任何缓存都被刷新

  2. # 2 楼答案

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

    考虑这个简化的例子;

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

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

    要解决这个问题,我们需要确保读写都在一个步骤中进行。我们可以将两者放在同一个同步块中

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

    由于同步块中的所有内容都被视为一个巨大的原子操作,因此读/写也是原子操作,线程安全也得以实现(因此,每次标志设置为true时,单个线程只会调用一次foo)


    AtomicBoolean(如另一个答案中所述)也可用于这种情况,因为此类提供了在同一步骤中读取和写入的操作。例如

    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();
       }
    }
    

    作为旁注,“getAndSet”操作在功能上等同于这样的操作

    public boolean getAndSet(boolean newVal)
    {
       synchronized(this)
       {
          boolean val = oldVal;
          oldVal = newVal;
          return val;
       }
    }
    

    然而,它以一种高度优化的方式来实现这一点,因此通常比使用synchronized关键字要快得多

  3. # 3 楼答案

    是的,对布尔值的并发修改需要同步,请使用原子布尔或同步构造

    此外,如果不在此处拥有监视器,则无法调用notifyAll():

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

    所以你有两个理由把它包装在同步块中