在Effective Java一书中,它指出:
语言规范保证读取或写入变量是原子的,除非变量是类型
long或double[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)
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在其间观察) .