关于Java:“atomic” 在编程中意味着什么?

What does “atomic” mean in programming?

在有效的Java书中,它指出:

The language specification guarantees that reading or writing a
variable is atomic unless the variable is of type long or double [JLS,
17.4.7].

"原子"在Java编程或编程中意味着什么?


这里有一个例子,因为一个例子往往比一个长的解释更清楚。假设foolong类型的变量。以下操作不是原子操作:

1
foo = 65465498L;

实际上,这个变量是用两个独立的操作来写的:一个写前32位,另一个写后32位。这意味着另一个线程可能读取EDOCX1的值(0),并看到中间状态。

使操作原子化包括使用同步机制,以确保从任何其他线程将操作视为单个原子操作(即不可拆分为多个部分)。这意味着一旦操作成为原子操作,任何其他线程将在分配之前或之后看到EDOCX1的值(0)。但绝不是中间值。

这样做的一个简单方法是使变量不稳定:

1
private volatile long foo;

或者同步对变量的每个访问:

1
2
3
4
5
6
7
8
public synchronized void setFoo(long value) {
    this.foo = value;
}

public synchronized long getFoo() {
    return this.foo;
}
// no other use of foo outside of these two methods, unless also synchronized

或者用AtomicLong替换:

1
private AtomicLong foo;


"原子操作"是指从所有其他线程的角度来看似乎是即时的操作。当担保适用时,您不必担心部分完成的操作。


这是"系统的其他部分似乎是瞬间发生的",属于计算过程中线性化的范畴。要进一步引用该链接文章:

Atomicity is a guarantee of isolation from concurrent processes.
Additionally, atomic operations commonly have a succeed-or-fail
definition — they either successfully change the state of the system,
or have no apparent effect.

因此,例如,在数据库系统的上下文中,可以有"原子提交",这意味着您可以将更新的变更集推送到关系数据库,并且这些更改要么全部提交,要么在失败的情况下全部不提交,这样数据就不会损坏,并且不会导致锁和/或队列,接下来操作将是一个不同的写入或读取,但只有在事实之后。在变量和线程的上下文中,这与应用于内存的情况大致相同。

您的报价强调,在所有情况下,这不必是预期的行为。


刚刚发现后原子操作和非原子操作对我很有帮助。

"An operation acting on shared memory is atomic if it completes in a single step relative to other threads.

When an atomic store is performed on a shared memory, no other thread can observe the modification half-complete.

When an atomic load is performed on a shared variable, it reads the entire value as it appeared at a single moment in time."


如果有多个线程执行下面代码中的M1和M2方法:

1
2
3
4
5
6
class SomeClass {
    private int i = 0;

    public void m1() { i = 5; }
    public int m2() { return i; }
}

您可以保证任何调用m2的线程将读取0或5。

另一方面,使用此代码(其中i是长的):

1
2
3
4
5
6
class SomeClass {
    private long i = 0;

    public void m1() { i = 1234567890L; }
    public long m2() { return i; }
}

调用m2的线程可以读取0、1234567890L或其他随机值,因为声明i = 1234567890L对于long不保证是原子的(jvm可以在两个操作中写入前32位和后32位,线程可以在两个操作之间观察i)。