如何根据布尔值同步代码?

问题描述

我有代码

    private volatile boolean immortal;
    private Object lock = new Object();
    
    public void set(boolean immortal) {
        this.immortal = immortal;
    }

    public void kill() {
       // .... contains some other code. 
       synchronized(lock) {
            if (!immortal) {
               for (int i = 0; i < numThreads; i++) {
                   runnableList.add(POISON_PILL);
               }
            }
       }
    }

我的用例是,我希望kill方法中的if语句在更改immortal值之前运行完毕。有没有一种更好的方法可以不锁定对象呢?

我的意思是,仅当布尔变量的值为false且不允许布尔值一直运行到完成时才对块进行同步的最佳方法是什么?我可以使用AtomicBoolean来实现吗?

解决方法

一种巧妙的方法是将runnableList声明为同步列表:

// where T is whatever type it needs to be
List<T> runnableList = Collections.synchronizedList(new ArrayList<>());

然后您可以添加它而无需显式同步:

if (!immortal) {
  runnableList.addAll(Collections.nCopies(numThreads,POISON_PILL));
}

之所以可行,是因为对addAll的单个调用是原子的。

这不是在没有同步的情况下完成的,而是在列表的内部。


这样说,很难建议一个“更好”的解决方案,因为尚不清楚要求是什么。同步(etc)用于在由多个线程操作时保留对象的不变性。

例如,为什么在将内容添加到immortal时需要runnableList保持不变?您还如何访问immortalrunnableList?等等

,

使用两个锁:

private boolean immortal;
private final Object killMonitor = new Object();
private final Object flagMonitor = new Object();

public void set(boolean immortal) {
    synchronized (flagMonitor) {
        this.immortal = immortal;
    }
}

public void kill() {
    // ...
    synchronized (flagMonitor) {
        if (!immortal) {
            synchronized (killMonitor) {
                runnableList.addAll(Collections.nCopies(numThreads,POISON_PILL));
            }
        }
    }
}