编程中的“原子”是啥意思?
Posted
技术标签:
【中文标题】编程中的“原子”是啥意思?【英文标题】:What does "atomic" mean in programming?编程中的“原子”是什么意思? 【发布时间】:2013-02-09 20:04:06 【问题描述】:在 Effective Java 一书中,它指出:
语言规范保证读或写 变量是原子的,除非变量的类型为
long
或double
[JLS, 17.4.7]。
在 Java 编程或一般编程的上下文中,“原子”是什么意思?
【问题讨论】:
一次一个操作。 一次只能对变量执行一个操作。 ibm.com/developerworks/library/j-jtp11234 我怀疑哲学问题属于codereview.stackexchange.com 注意到一些变量默认情况下没有原子读写,将它们声明为volatile long
或volatile double
使得读原子和写原子。
【参考方案1】:
这是“在系统的其余部分看来是瞬间发生的”,在计算过程中属于Linearizability 的分类。进一步引用链接的文章:
原子性是与并发进程隔离的保证。 此外,原子操作通常有成败 定义——它们要么成功地改变了系统的状态,要么 或者没有明显的效果。
因此,例如,在数据库系统的上下文中,可以有“原子提交”,这意味着您可以将更新的变更集推送到关系数据库,这些变更要么全部提交,要么都不提交在发生故障的情况下,数据不会损坏,并且锁和/或队列的结果,下一个操作将是不同的写入或读取,但仅在事实发生之后 .在变量和线程的上下文中,这几乎是相同的,适用于内存。
您的引述强调了这不是在所有情况下都是预期的行为。
【讨论】:
【参考方案2】:举个例子:假设foo
是long
类型的变量,那么下面的操作就不是原子操作(在Java中):
foo = 65465498L;
确实,变量是使用两个单独的操作写入的:一个写入前 32 位,第二个写入最后 32 位。这意味着另一个线程可能会读取foo
的值,并查看中间状态。
使操作原子化包括使用同步机制,以确保从任何其他线程将操作视为单个原子(即不可拆分为部分)操作。这意味着任何其他线程,一旦操作成为原子操作,将在分配之前或之后看到foo
的值。但绝不是中间值。
一个简单的方法是制作variable 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;
【讨论】:
所以这是假设它在 32 位系统中运行。如果是 64 位系统呢?将 foo = 65465498L;那么是原子的? @Harke 如果你运行的是 64 位 Java,是的。 这是否也适用于 C# 和 .NET?如果是,为了让 foo 获得原子行为,CLR 必须是 64 位的? @F***o 它确实适用,这里是如何在 .NET 中实现它,因为我们没有像 Java 这样的同步关键字。 ***.com/questions/541194/… 然后让我们假设线程 A 分配 long 值,然后在中途线程 B 尝试读取它。如果操作A是原子的,那么线程B会等到它完成吗?这意味着原子操作将提供隐式线程安全?【参考方案3】:如果您有多个线程执行下面代码中的方法 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 或其他一些随机值,因为对于 long
语句 i = 1234567890L
不能保证是原子的(JVM 可以写入前 32 位和后 32 位)两个操作中的位和一个线程可能会在两者之间观察到i
)。
【讨论】:
为什么你认为“long”会导致问题,而“int”不会?请看这里geekswithblogs.net/BlackRabbitCoder/archive/2012/08/09/… @entropy long 和 double 赋值在 Java 中不保证是原子的。因此,您可以阅读很长的内容,其中只有一半的位在分配后被更新。【参考方案4】:“原子操作”是指从所有其他线程的角度来看似乎是瞬时的操作。当保证适用时,您无需担心部分完成的操作。
【讨论】:
【参考方案5】:刚刚发现一个帖子Atomic vs. Non-Atomic Operations对我很有帮助。
"如果一个对共享内存的操作相对于其他线程来说是一步完成的,那么它就是原子的。
当在共享内存上执行原子存储时,没有其他线程可以观察到修改半完成。
当对共享变量执行原子加载时,它会读取在某个时刻出现的整个值。”
【讨论】:
【参考方案6】:在 Java 中,除了 long 和 double 之外的所有类型的字段都是原子读写的,如果使用 volatile 修饰符声明该字段,即使 long 和 double 也是原子读写的。也就是说,我们得到 100% 的结果,或者那里发生了什么,也不能在变量中有任何中间结果。
【讨论】:
以上是关于编程中的“原子”是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章