Java中Atomic set和volatile set有什么区别?

4

假设我有以下两个类。

public class Foo {
   private volatile Integer x = 0;
   private volatile boolean istrue = false;

   public void setInt(int x) {
       this.x = x;
   }

   public void setBoolean(boolean istrue) {
       this.istrue = istrue;
   }

   public Integer getInt() {
       return x;
   }
}

vs

public class Bar {
   private volatile AtomicInteger x = 0;
   private volatile AtomicBoolean istrue = false;

   public void setInt(int x) {
       this.x.set(x);
   }

   public void setBoolean(boolean istrue) {
       this.istrue.set(istrue);
   }

   public Integer getInt() {
       return this.x.get();
   }
}

假设多个线程可以访问Foo或Bar类,这两个类都是线程安全的吗?这两个类之间的区别是什么?


2
根据您编写的方法,没有区别。当您尝试以原子方式执行更复杂的操作时,差异就会出现。 - Louis Wasserman
1
第二个示例的代码将无法编译。您不能将诸如 0false 等原始值分配给 AtomicIntegerAtomicBoolean。对于这些类型,没有自动装箱。 - Jesper
我想知道“线程安全”是否意味着你认为的那样?如果两个或多个线程的重叠调用不能将类的数据置于任何不良状态或创建不良状态的外观,则该类是“线程安全”的。你的FooBar类允许哪些状态?哪些状态是不允许的?这里是“线程安全”不意味着的内容。它并不意味着在程序中使用此类将使您的程序线程安全。 - Solomon Slow
3个回答

1
在当前示例中,您的方法中只有单个赋值语句。我认为您需要提供一个更好的示例。因为该赋值将在单个指令中执行。这使得实现线程安全。尽管如此,当访问它们时,两个不同的线程可能会看到不同的int值,因为其他线程可以潜在地重置(设置)为不同的值。
查看此内容:Java中的线程安全是什么?

1

这两个类的编写都是线程安全的。使用volatile变量和java.util.concurrent.atomic类之间的区别在于,原子类允许执行更复杂的操作,例如比较和设置,以原子方式执行。只有直接访问volatile变量时,在比较和设置之间可能会发生线程上下文切换,因此不能依赖于比较结果在设置完成后仍然有效。


1

这两个类都是线程安全的。但这不是你在这里面遇到的问题。

Foo:对字段的读写是原子的,但更复杂的操作如 i++ 不是。i++ 转换为 i = i + 1,它分解成读和写两个步骤,因此可能会在其中发生线程上下文切换,使整个操作不是原子的。

Bar:volatile 使对 Atomic 字段本身的访问是原子的,因此您可以以原子方式重新分配具有新引用的字段。但是,对字段的操作,如 compareAndSetinc 是原子的。问题是,您需要对字段进行原子写入访问还是只需要对操作进行原子性?由于 AtomicXXX 类型只是值的容器,因此通常不会重新分配变量,而是重新分配值,这是原子的。

所以这应该足够了:

private final AtomicInteger x = new AtomicInteger(0);
private final AtomicBoolean istrue = new AtomicBoolean(false);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接