Java 顺序一致性和原子性之间有什么区别?

Java 顺序一致性和原子性之间有什么区别?,java,c++,atomic,volatile,Java,C++,Atomic,Volatile,我读到java volatile是顺序一致的,但不是原子的。 对于原子性,java提供了不同的库 有人能用简单的英语解释一下两者的区别吗 (我相信问题的范围包括C/C++并因此添加这些语言标记以获得更多的受众。)假设您有以下两个变量: public int a = 0; public volatile int b = 0; synchronized (lock) { a = 1; b = 2; } ... synchronized (lock) { System.o

我读到java volatile是顺序一致的,但不是原子的。 对于原子性,java提供了不同的库

有人能用简单的英语解释一下两者的区别吗


(我相信问题的范围包括C/C++并因此添加这些语言标记以获得更多的受众。)

假设您有以下两个变量:

public int a = 0;
public volatile int b = 0;
synchronized (lock) {
    a = 1;
    b = 2;
}

...

synchronized (lock) {
    System.out.println("a = " + a + "; b = " + b);
}
假设有一个线程可以

a = 1;
b = 2;
如果另一个线程读取这些值并看到b==2,那么它保证也会看到a==1

但是读取线程可以看到
a==1
b==0
,因为这两次写入不是原子操作的一部分,所以读取线程可能会在第一个线程为
b
赋值之前看到对
a
所做的更改

要使这两次写入原子化,您需要同步对这两个变量的访问:

public int a = 0;
public volatile int b = 0;
synchronized (lock) {
    a = 1;
    b = 2;
}

...

synchronized (lock) {
    System.out.println("a = " + a + "; b = " + b);
}

在这种情况下,读取线程将看到
a==0
b==0
,或
a==1
b==2
,但不会看到中间状态。

想象一下类中的这两个变量:

int i = 0;
volatile int v = 0;
那两种方法呢

void write() {
    i = 5;
    v = 2;
}

void read() {
    if (v == 2) { System.out.println(i); }
}
volatile语义保证
read
要么打印5,要么不打印(当然,假设没有其他方法修改字段)。如果
v
不是易失性的,
read
也可以打印0,因为
i=5
v=2
可能已经重新排序。我想这就是你所说的顺序一致性的意思,它有更广泛的含义

另一方面,volatile不能保证原子性。因此,如果两个线程同时调用此方法(v是相同的
volatile int
):

您不能保证v将增加2。这是因为
v++
实际上是三条语句:

load v;
increment v;
store v;

由于线程交错,v只能增加一次(两个线程将加载相同的值)。

+1,但你并不是说“这是顺序一致性”和“这是原子性”。是的,我认为这是显而易见的。但assylias的答案比我的答案更清楚,也更好。我不认为
volatile
在C/C++中提供了与Java相同的保证。特别是,我不认为它在C中提供了你所说的顺序一致性(即内存界限适用于其他变量)——TBC。@assylias我不是说volatile在C/C++中有相同的含义。我相信不会的。我指的是c/c++和Java之间常见的“顺序一致性和原子性”概念。在C/C++中,volatile关键字停止变量使用的优化,不用于维护在使用volatile变量进行操作之前指定的操作之间的顺序一致性。如果您可以添加一个示例来演示原子和顺序的内容,这将是完整的答案。我们可以不使用互斥锁就这样做吗?现在无法更新,但是可以,所以cslled无锁算法使用CAS操作来提供原子性。java中的所有AtomicXXX类都使用它。