我经常使用以下模式创建一个可取消的线程:
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。在这种情况下我们可以只使用普通的布尔值吗?
答案 0 :(得分:2)
您可以使用volatile boolean
而不会出现问题。
请注意,这仅适用于这样的情况,其中布尔值仅被更改为特定值(在这种情况下为true
)。如果布尔值可能随时更改为true
或false
,那么您可能需要AtomicBoolean
来检测并根据竞争条件采取行动。
但是 - 你描述的模式有一种天生的气味。通过循环布尔(volatile
或不),您可能会发现自己试图插入某种sleep
机制或不得不中断线程。
更清洁的方法是将流程分解为更精细的步骤。我最近发布了一个答案here,涵盖了可能感兴趣的暂停线程的选项。
答案 1 :(得分:2)
不需要同时使用volatile
和AtomicBoolean
。如果您将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;
}