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

Jam*_*mes 250 java atomic

在Effective Java一书中,它指出:

语言规范保证读取或写入变量是原子的,除非变量是类型longdouble[JLS,17.4.7].

"原子"在Java编程或一般编程的上下文中意味着什么?

JB *_*zet 345

这是一个例子,因为一个例子通常比一个长的解释更清楚.假设foo是一个类型的变量long.以下操作不是原子操作:

foo = 65465498L;
Run Code Online (Sandbox Code Playgroud)

实际上,变量是使用两个独立的操作写入的:一个写入前32位,另一个写入最后32位.这意味着另一个线程可能会读取其值foo,并看到中间状态.

使操作原子化包括使用同步机制,以确保从任何其他线程看到操作为单个原子(即不可拆分的部分)操作.这意味着任何其他线程,一旦操作成为原子操作,就会看到foo赋值之前或赋值之后的值.但绝不是中间价值.

这样做的一个简单方法是使变量volatile:

private volatile long foo;
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

或者用以下内容替换它AtomicLong:

private AtomicLong foo;
Run Code Online (Sandbox Code Playgroud)

  • 所以这是假设它在32位系统中运行.如果它是64位系统怎么办?请问foo = 65465498L; 是原子的吗? (67认同)
  • @Harke如果你运行64位Java,是的. (41认同)
  • @Fabiano它确实适用,这里是如何在.NET中实现它,因为我们没有像Java这样的synchronized关键字.http://stackoverflow.com/questions/541194/c-sharp-version-of-javas-synchronized-keyword (5认同)
  • 这是否也适用于C#和.NET?如果是,为了使foo获得原子行为,CLR必须是64位? (3认同)
  • 然后,假设线程A分配了long值,然后线程B尝试以一半的方式读取它。如果操作A是原子操作,那么线程B将等待直到完成?这意味着原子操作将提供隐式线程安全性吗? (2认同)

H2O*_*aCl 54

"原子操作"是指从所有其他线程的角度来看似乎是瞬时的操作.保证适用时,您无需担心部分完成的操作.


Gra*_*mas 23

它"在系统的其他部分看来是即时发生的",并且属于计算过程中线性化的分类.要进一步引用该链接文章:

原子性是与并发进程隔离的保证.此外,原子操作通常具有成功或失败的定义 - 它们要么成功地改变系统的状态,要么没有明显的效果.

因此,例如,在数据库系统的上下文中,可以进行"原子提交",这意味着您可以将更新的更改集推送到关系数据库,并且这些更改将全部提交,或者根本不提交任何更改.如果发生故障,以这种方式数据不会被破坏,并且锁定和/或队列的结果,下一个操作将是不同的写入或读取,但仅事后.在变量和线程的上下文中,这与应用于内存大致相同.

您的引用强调,在所有情况下,这不一定是预期的行为.


ass*_*ias 14

如果您有多个线程在下面的代码中执行方法m1和m2:

class SomeClass {
    private int i = 0;

    public void m1() { i = 5; }
    public int m2() { return i; }
}
Run Code Online (Sandbox Code Playgroud)

你有保证任何线程调用m2将读取0或5.

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

class SomeClass {
    private long i = 0;

    public void m1() { i = 1234567890L; }
    public long m2() { return i; }
}
Run Code Online (Sandbox Code Playgroud)

一个线程调用m2可以读取0,1234567890L或其他一些随机值,因为该语句i = 1234567890L不能保证是a的原子long(JVM可以在两个操作中写入前32位和最后32位,并且线程可能i在其间观察) .


Kur*_*ong 14

刚发现原子与非原子操作的帖子对我很有帮助.

"如果相对于其他线程在一个步骤中完成,则对共享内存起作用的操作是原子操作.

当在共享内存上执行原子存储时,没有其他线程可以观察到修改半完成.

当对共享变量执行原子加载时,它会读取单个时刻出现的整个值."