【问题标题】:Why is using atomic variable access is more efficient than accessing these variable through synchronised code? [duplicate]为什么使用原子变量访问比通过同步代码访问这些变量更有效? [复制]
【发布时间】:2016-05-30 14:52:26
【问题描述】:
我是 Java 新手,正在尝试学习原子访问的概念。我从 Java Tutorial Oracle 中看到了下面的语句。我的问题是:
为什么使用原子变量访问比通过同步代码访问这些变量更有效?
为什么使用原子变量访问需要程序员更加小心以避免内存一致性错误。
我很难理解它。
使用简单的原子变量访问比访问更高效
这些变量通过同步代码,但需要更加小心
程序员避免内存一致性错误。是否额外
努力是否值得取决于项目的规模和复杂性
应用。
【问题讨论】:
标签:
java
synchronization
shared-memory
atomic
synchronized
【解决方案1】:
回答(1):
对于读访问,无论是原子的还是非原子的、同步的还是非同步的都没有关系。
对于写访问,原子变量不需要写锁,因为对变量的所有更新都是原子的(发生或未完全发生)例如:假设您想在多线程应用程序中执行 i++ 并且多个线程可以调用它,您需要同步 i++ 调用(因为它设置为 3 个注册表级别调用,并且您知道它可以在注册表级别调用的任何点进行上下文切换,甚至在两者之间)以避免脏读和不一致的写入。
而 Atomic 变量只有 1 个注册表级别调用(由于添加了额外的寄存器以及我们的语言利用它)。
由于同步开销(获取监视器锁并随后释放),原子变量访问比通过同步代码访问这些变量更有效
回答(2):
Java中的所有对象都需要程序员更加小心以避免内存一致性错误,无论它是否是Atomic。
虽然 Atomic 变量的兄弟姐妹可以是原始变量而不是对象,但它不属于对象类别,因此不需要处理内存一致性错误
每个对象都需要避免多线程应用程序中的内存一致性错误的原因是因为每个线程堆栈在线程堆栈上本地缓存对象的副本(运行时优化)如果它被另一个线程修改,可能会导致与堆的实际副本不同步(即使在相同的代码但不同的线程堆栈中)。要避免的一种解决方案是使用 volatile 来表示可以被另一个线程频繁更改的对象。此外,本地副本尝试非常快地与堆副本同步,但如果您的线程访问它的速度比同步发生的速度快,就会出现问题。
希望这有助于您理解原子和内存访问的概念。
【解决方案2】:
将变量声明为 volatile 意味着修改其值会立即影响变量的实际内存存储。编译器无法优化对变量所做的任何引用。这保证了当一个线程修改变量时,所有其他线程立即看到新值。 (非易失性变量不一定如此。)
声明一个原子变量可以保证对变量进行的操作以原子方式发生,即操作的所有子步骤都在它们执行的线程内完成,并且不会被其他线程中断。例如,增量和测试操作要求变量递增,然后与另一个值进行比较;原子操作保证这两个步骤都将像单个不可分割/不可中断的操作一样完成。
同步对变量的所有访问,一次只允许一个线程访问该变量,并强制所有其他线程等待该访问线程释放其对变量的访问。
同步访问类似于原子访问,但原子操作一般是在较低的编程层次上实现的。此外,完全可以只同步对变量的某些访问,而允许其他访问不同步(例如,同步对变量的所有写入,但不同步对变量的读取)。
原子性、同步性和易变性是独立的属性,但通常结合使用以强制执行适当的线程协作以访问变量。