在《Effective Java》一书中,它指出:
语言规范保证读写 变量是原子的,除非变量类型为long或double [JLS, 17.4.7]。
“原子”在Java编程或一般编程上下文中是什么意思?
在《Effective Java》一书中,它指出:
语言规范保证读写 变量是原子的,除非变量类型为long或double [JLS, 17.4.7]。
“原子”在Java编程或一般编程上下文中是什么意思?
当前回答
这里有一个例子:假设foo是一个long类型的变量,那么下面的操作不是一个原子操作(在Java中):
foo = 65465498L;
实际上,这个变量是用两个独立的操作写的:一个写前32位,另一个写后32位。这意味着另一个线程可以读取foo的值,并看到中间状态。
使操作原子化包括使用同步机制,以确保从任何其他线程中,操作被视为单个原子(即不可分割为部分)操作。这意味着,一旦操作成为原子操作,任何其他线程都将在赋值之前或赋值之后看到foo的值。但从来没有中间值。
一个简单的方法是使变量为volatile:
private volatile long foo;
或者同步对变量的每次访问:
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:
private AtomicLong foo;
其他回答
这是“对系统的其余部分来说,似乎是瞬间发生的”,并且属于计算过程中的线性化范畴。进一步引用这篇链接文章:
原子性是与并发进程隔离的保证。 此外,原子操作通常具有成功或失败 定义-他们要么成功地改变了系统的状态, 或者没有明显的效果。
So, for instance, in the context of a database system, one can have 'atomic commits', meaning that you can push a changeset of updates to a relational database and those changes will either all be submitted, or none of them at all in the event of failure, in this way data does not become corrupt, and consequential of locks and/or queues, the next operation will be a different write or a read, but only after the fact. In the context of variables and threading this is much the same, applied to memory.
你的引用强调了这并不需要在所有情况下都是预期行为。
“原子操作”是指从所有其他线程的角度来看是瞬时的操作。当保证生效时,您不需要担心部分完成的操作。
刚刚发现一篇关于原子与非原子操作的文章对我很有帮助。
作用于共享内存的操作是原子的,如果它相对于其他线程在一个步骤中完成。 当在共享内存上执行原子存储时,没有其他线程可以观察到修改已完成一半。 当对共享变量执行原子加载时,它会读取该变量在某一时刻出现的整个值。”
这里有一个例子:假设foo是一个long类型的变量,那么下面的操作不是一个原子操作(在Java中):
foo = 65465498L;
实际上,这个变量是用两个独立的操作写的:一个写前32位,另一个写后32位。这意味着另一个线程可以读取foo的值,并看到中间状态。
使操作原子化包括使用同步机制,以确保从任何其他线程中,操作被视为单个原子(即不可分割为部分)操作。这意味着,一旦操作成为原子操作,任何其他线程都将在赋值之前或赋值之后看到foo的值。但从来没有中间值。
一个简单的方法是使变量为volatile:
private volatile long foo;
或者同步对变量的每次访问:
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:
private AtomicLong foo;
如果你有几个线程执行下面代码中的方法m1和m2:
class SomeClass {
private int i = 0;
public void m1() { i = 5; }
public int m2() { return i; }
}
你可以保证任何调用m2的线程读0或5。
另一方面,对于下面的代码(其中i是长):
class SomeClass {
private long i = 0;
public void m1() { i = 1234567890L; }
public long m2() { return i; }
}
调用m2的线程可以读取0、1234567890L或其他一些随机值,因为语句i = 1234567890L不能保证长时间都是原子的(JVM可以在两次操作中写入前32位和后32位,线程可能会在中间观察到i)。