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

Java 创建可取消线程是否需要AtomicBoolean?,java,multithreading,atomic,volatile,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 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一定是原子布尔。在这种情况下,我们可以只使用普通布尔值吗?

不,不能。因为如果在没有正确同步的情况下从另一个线程更改布尔值,那么这个更改对另一个线程是不可见的。您可以在本例中使用
valotile boolean
,使任何修改对所有线程都可见

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

您可以使用
易失性布尔值
,而不会出现任何问题

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

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


一个更干净的方法是将过程分成更精细的步骤。我最近发布了一个答案,涵盖了暂停可能感兴趣的线程的选项。

您可以使用
volatile boolean
,而无需任何问题

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

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


一个更干净的方法是将过程分成更精细的步骤。我最近发布了一个答案,涵盖了暂停可能感兴趣的线程的选项。

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


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

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


根据java内存模型(),两个选项都会产生一个正确同步的程序,其中取消注销变量的读写不能产生数据竞争。

在这个上下文中使用一个易失性布尔变量是安全的,尽管有些人可能认为它是坏的实践。咨询一下原因

使用原子*变量的解决方案似乎是最好的选择,尽管与易失性变量相比,同步可能会引入不必要的开销

您还可以使用临界截面

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

在这个上下文中使用一个易失性布尔变量是安全的,尽管有些人可能认为它是错误的实践。咨询一下原因

使用原子*变量的解决方案似乎是最好的选择,尽管与易失性变量相比,同步可能会引入不必要的开销

您还可以使用临界截面

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

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

private final AtomicBoolean cancelPending = new AtomicBoolean(false);
final
字段的JLS语义意味着不需要同步(或
volatile
)。所有线程都将看到
cancelPending
引用的正确值。国家:

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

。。。但对正常场没有这样的保证;i、 e.非
final
和非
volatile

您也可以将
cancelPending
声明为
volatile boolean
。。。因为您似乎没有使用原子布尔的测试和设置功能


但是,如果使用非易失性
布尔值
,则需要使用
同步
,以确保所有线程都能看到
取消挂起
标志的最新副本。

不必同时使用
易失性
原子布尔值
。如果将
cancelPending
变量声明为
final
,如下所示:

private final AtomicBoolean cancelPending = new AtomicBoolean(false);
final
字段的JLS语义意味着不需要同步(或
volatile
)。所有线程都将看到
cancelPending
引用的正确值。国家:

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

。。。但对正常场没有这样的保证;i、 e.非
final
和非
volatile

您也可以将
cancelPending
声明为
volatile boolean
。。。因为你似乎没有使用t