什么是易失性变量的内存存储?

13 浏览
0 Comments

什么是易失性变量的内存存储?

我已阅读了《在Java中何时使用“ volatile”?》但仍感到困惑。我如何知道何时应将变量标记为volatile?如果我弄错了,要么在需要的地方省略了volatile,要么在不需要的地方使用了volatile,会发生什么?在确定多线程代码中哪些变量应该是volatile时,有什么经验法则?

0
0 Comments

volatile关键字在无锁算法中非常有用。当您在访问共享数据时不使用锁,并且希望一个线程所做的更改在另一个线程中可见时,您将标记保存共享数据的变量为volatile,或者您想要创建一个“happens-after”关系来确保计算不被重新排序,再次确保更改在适当的时间可见。

JMM Cookbook描述了哪些操作可以重新排序,哪些操作不可以重新排序。

volatile关键字的出现是为了解决多线程环境下的可见性和有序性问题。在多线程环境中,每个线程都有自己的线程缓存,当一个线程修改了某个变量的值时,这个修改可能只会保存在该线程的线程缓存中,并没有立即写回主存。这就导致了一个线程对某个变量的修改对其他线程不可见,从而引发了可见性问题。

为了解决可见性问题,我们可以使用volatile关键字。当一个变量被volatile修饰时,对该变量的修改将立即写回主存,而不是仅保存在线程缓存中。这样,其他线程在读取该变量时就可以从主存中获取最新的值,从而解决了可见性问题。

此外,volatile关键字还可以解决有序性问题。在多线程环境中,由于编译器和处理器的优化,指令的执行顺序可能会被重新排序。这在大多数情况下不会影响单线程的执行结果,但在多线程环境中可能会引发问题。使用volatile关键字可以禁止指令的重新排序,从而保证指令的执行顺序与代码的顺序一致,解决了有序性问题。

总结起来,volatile关键字的出现是为了解决多线程环境下的可见性和有序性问题。通过使用volatile关键字,可以保证对volatile变量的修改立即写回主存,从而解决了可见性问题;同时,可以禁止指令的重新排序,保证指令的执行顺序与代码的顺序一致,解决了有序性问题。

0
0 Comments

volatile关键字保证了volatile变量的值始终从主内存中读取,而不是从线程的本地缓存中读取。

根据Java并发教程:

使用volatile变量可以减少内存一致性错误的风险,因为对volatile变量的任何写操作都会与后续对该变量的读操作建立happens-before关系。

这意味着对volatile变量的更改始终对其他线程可见。这也意味着当一个线程读取一个volatile变量时,它不仅可以看到volatile的最新更改,还可以看到导致该变化的代码的副作用。

关于你的问题:

如何知道什么时候应该将变量标记为volatile?在多线程代码中确定哪些变量应该是volatile的规则是什么?

如果你觉得所有的读取线程总是获取变量的最新值,那么你必须将变量标记为volatile。

如果你有一个写线程来修改变量的值,而有多个读取线程来读取变量的值,volatile修饰符保证了内存一致性。

如果你有多个线程来写入和读取变量,仅仅使用volatile修饰符不保证内存一致性。你必须同步代码或使用高级并发构造,如锁、并发集合、原子变量等。

相关的SE问题/文章:

- [Volatile variable explanation in Java docs](https://stackoverflow.com/questions/19738651)

- [Difference between volatile and synchronized in Java](https://stackoverflow.com/questions/3519664)

- [javarevisited](http://javarevisited.blogspot.com/2011/06/volatile-keyword-java-example-tutorial.html)文章

0
0 Comments

volatile变量在多线程环境下被访问时,当不需要复合原子性(不确定是否为正确的术语)时使用。上述的第一个例子是一个糟糕的例子,因为你需要复合原子性。解决方法是使用synchronized关键字来确保只有一个线程可以执行操作。

第二个例子是一个有效的例子,使用了volatile关键字来确保对变量的修改可以在其他线程中可见。

为什么不能只使用private static int temperature呢?事实上你可以这样做(在程序不会崩溃或其他异常情况下),但是另一个线程对temperature的改变可能对主线程不可见。这意味着如果不使用volatile,你的应用程序可能会一直打印"Today's temperature is 0",尽管实际上它最终会变得可见。然而,你不应该冒险不使用volatile,因为它可能会导致严重的错误(例如不完全构造的对象等)。

如果在不需要volatile的地方使用volatile关键字,不会影响代码的正确性(即行为不会改变)。在性能方面,它将取决于JVM的实现。理论上,由于编译器无法进行重新排序优化,需要使CPU缓存无效等,你可能会获得微小的性能降低,但是编译器可以证明你的字段永远不会被多个线程访问,并且完全删除volatile关键字的效果,并将其编译为相同的指令。

可以使用synchronized关键字和同步的getter方法来替代volatile,它们会产生相同的效果。使用volatile的优势有两个原因:

1. 更少的错误:这取决于上下文,但在许多情况下,使用volatile会更少出现并发错误,例如在持有锁时阻塞,死锁等。

2. 性能更好:在大多数JVM实现中,volatile的吞吐量和延迟都比较高。然而,在大多数应用程序中,差异太小,无关紧要。

对于volatile和synchronized的选择,这个回答让我完全明白了,非常感谢。

然而,为什么不能使用synchronized关键字来创建一个同步的getter方法呢?

:在这里添加了一个答案。希望对你有帮助。

对我来说,同步的getter方法如何有助于解决这个问题还不清楚。如果不使用volatile,我们仍然无法保证线程本地缓存。

Antario: synchronized提供的保证是volatile提供的保证的超集,因此在使用synchronized的同时使用volatile是多余的。

是的,我已经在这里提出过这个问题。

进一步加深理解:定义既是volatile又是final的字段是一个编译错误。这是没有意义的,因为final变量不能重新分配。

:我搜索了原子复合语句,但仍然无法理解这个概念。你能解释一下在这个上下文中什么是原子复合吗?

Akshayraj Kore,当他说"compound atomicity"时,他是指需要作为一个整体原子操作的多个操作。例如,在同步方法或同步代码块中放置的所有代码。另一个例子是i++,因为它是3个操作:读取、递增、写入。将i++放在同步代码块中可以将这3个操作作为一个整体进行原子操作。

0