1

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

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。在这种情况下我们可以只使用普通的布尔值吗?

4

5 回答 5

2

您可以使用 avolatile boolean而不是没有问题。

请注意,这仅适用于类似于这种情况的情况,即布尔值仅更改为特定值(true在本例中)。如果布尔值可能会更改为任一truefalse任何时候,那么您可能需要AtomicBoolean检测并处理竞争条件。

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

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

于 2013-02-14T12:48:56.557 回答
2

同时使用volatileandAtomicBoolean是不必要的。如果按如下方式声明cancelPending变量:final

private final AtomicBoolean cancelPending = new AtomicBoolean(false);

字段的 JLS 语义final意味着volatile不需要同步(或)。所有线程都将看到正确的cancelPending参考值。JLS 17.5规定:

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

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

您也可以只声明cancelPendingvolatile boolean... 因为您似乎没有使用AtomicBoolean.

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

于 2013-02-14T12:55:07.590 回答
0

你不能。因为如果您将在没有适当同步的情况下从另一个线程更改布尔值,那么此更改对其他线程可能是不可见的。您可以valotile boolean在您的情况下使用任何修改对所有线程可见。

于 2013-02-14T12:48:17.027 回答
0

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

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

于 2013-02-14T12:49:38.887 回答
0

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

您使用 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;
}
于 2013-02-14T12:52:53.863 回答