是否需要AtomicBoolean来创建可取消的线程?

时间:2013-02-14 12:47:01

标签: java multithreading atomic volatile

我经常使用以下模式创建一个可取消的线程:

public class CounterLoop implements Runnable {

    private volatile AtomicBoolean cancelPending = new AtomicBoolean(false);

    @Override
    public void run() {
        while (!cancelPending.get()) {
            //count
        }
    }

    public void cancel() {
        cancelPending.set(true);
    }
}

但我不确定cancelPending必须是AtomicBoolean。在这种情况下我们可以只使用普通的布尔值吗?

5 个答案:

答案 0 :(得分:2)

您可以使用volatile boolean而不会出现问题。

请注意,这仅适用于这样的情况,其中布尔值仅被更改为特定值(在这种情况下为true)。如果布尔值可能随时更改为truefalse,那么您可能需要AtomicBoolean来检测并根据竞争条件采取行动。

但是 - 你描述的模式有一种天生的气味。通过循环布尔(volatile或不),您可能会发现自己试图插入某种sleep机制或不得不中断线程。

更清洁的方法是将流程分解为更精细的步骤。我最近发布了一个答案here,涵盖了可能感兴趣的暂停线程的选项。

答案 1 :(得分:2)

不需要同时使用volatileAtomicBoolean。如果您将cancelPending变量声明为final,如下所示:

private final AtomicBoolean cancelPending = new AtomicBoolean(false);

final字段的JLS语义意味着不需要同步(或volatile)。所有线程都会看到cancelPending引用的正确值。 JLS 17.5州:

  

“当构造函数完成时,对象被认为是完全初始化的。在该对象完全初始化之后只能看到对象引用的线程可以保证看到该对象最终的正确初始化值域“。

......但是对于普通字段没有这样的保证;即不是final而不是volatile

您也可以将cancelPending声明为volatile boolean ...因为您似乎没有使用AtomicBoolean的测试和设置功能。<​​/ p>

但是,如果您使用了非易失性boolean,则需要使用synchronized来确保所有线程都能看到cancelPending标志的最新副本。 / p>

答案 2 :(得分:0)

不,你不能。因为如果您在没有正确同步的情况下从另一个线程更改布尔值,则此更改对于另一个线程是不可见的。您可以在案例中使用valotile boolean对所有线程进行任何修改。

答案 3 :(得分:0)

是的,你可以。您可以使用非易失性AtomicBoolean(依赖于其内置的线程安全性),也可以使用任何其他volatile变量。

根据Java内存模型(JMM),这两个选项都会导致正确同步的程序,其中cancelPending变量的读取和写入不会产生数据争用。

答案 4 :(得分:0)

在这种情况下使用volatile布尔变量是安全的,尽管有些人可能认为这是不好的做法。请咨询this thread以了解原因。

使用Atomic *变量的解决方案似乎是最佳选择,即使同步可能会与volatile变量相比带来不必要的开销。

您还可以使用关键部分

Object lock = new Object();

@Override
public void run() {
  synchronized (lock) {
    if (cancelPending) {
      return;
    }
  }
}

或同步方法。

synchronized public boolean shouldStop() {
  return shouldStop;
}

synchronized public void setStop(boolean stop) {
  shouldStop = stop;
}