静态意味着所有对象的值的一个副本,而volatile意味着所有线程的值的一个副本,这是正确的吗?

不管怎样,一个静态变量值对于所有线程来说都是一个值,那为什么我们要用volatile呢?


当前回答

除了其他的答案,我想为它添加一张图片(图片方便理解)

可以为单个线程缓存静态变量。在多线程环境中,如果一个线程修改了它的缓存数据,这可能不会反映给其他线程,因为他们有一个副本。

Volatile声明确保线程不会缓存数据,只使用共享副本。

图片来源

其他回答

如果将变量声明为静态,则该变量将只有一个副本。 因此,每当不同的线程访问该变量时,该变量将只有一个最终值(因为仅为该变量分配了一个内存位置)。

如果一个变量被声明为volatile,那么所有线程都将有自己的变量副本,但是值是从主存中取出的。因此,变量在所有线程中的值都是相同的。

因此,在这两种情况下,重点是变量的值在所有线程中是相同的。

不确定静态变量是否缓存在线程本地内存中。但是当我执行两个线程(T1,T2)访问同一个对象(obj)时,当T1线程对静态变量进行更新时,它在T2中得到了反映。

Static和Volatile的区别:

静态变量:如果两个线程(假设t1和t2)正在访问同一个对象,并更新一个被声明为静态的变量,那么这意味着t1和t2可以在各自的缓存中创建相同对象(包括静态变量)的本地副本,因此t1对其本地缓存中的静态变量的更新不会反映在t2缓存的静态变量中。

静态变量用于Object的上下文中,其中一个对象所做的更新将反映在同一类的所有其他对象中,但不用于Thread的上下文中,其中一个线程对静态变量的更新将立即反映到所有线程(在它们的本地缓存中)。

Volatile变量:如果两个线程(假设t1和t2)正在访问同一个对象,并更新一个声明为Volatile的变量,那么这意味着t1和t2可以创建他们自己的对象本地缓存,除了声明为Volatile的变量。因此volatile变量只有一个主副本,它将由不同的线程更新,一个线程对volatile变量的更新将立即反映到另一个线程。

除了其他的答案,我想为它添加一张图片(图片方便理解)

可以为单个线程缓存静态变量。在多线程环境中,如果一个线程修改了它的缓存数据,这可能不会反映给其他线程,因为他们有一个副本。

Volatile声明确保线程不会缓存数据,只使用共享副本。

图片来源

简单来说,

静态:静态变量与类相关联,而不是与任何对象相关联。类的每个实例共享一个类变量,该变量位于内存中的一个固定位置 volatile:该关键字同时适用于类变量和实例变量。

使用易失性变量可以降低内存一致性错误的风险,因为对易失性变量的任何写入都会与该变量的后续读取建立happens-before关系。这意味着对volatile变量的更改总是对其他线程可见

看看Javin Paul的这篇文章,以更好地理解volatile变量。

在没有volatile关键字的情况下,每个线程的堆栈中的variable值可能是不同的。通过将变量设置为volatile,所有线程将在其工作内存中获得相同的值,从而避免了内存一致性错误。

这里的术语变量可以是静态(类)变量,也可以是实例(对象)变量。

关于你的问题:

不管怎样,一个静态变量值对于所有线程来说都是一个值,那为什么我们要用volatile呢?

如果我需要实例变量在我的应用程序,我不能使用静态变量。即使在静态变量的情况下,由于如图所示的线程缓存,一致性也不能保证。

使用易失性变量可以降低内存一致性错误的风险,因为对易失性变量的任何写入都会与该变量的后续读取建立happens-before关系。这意味着对volatile变量的更改总是对其他线程可见。

更重要的是,这也意味着当线程读取一个volatile变量时,它不仅看到了对volatile的最新更改,而且还看到了导致change =>的代码的副作用,volatile变量仍然可能存在内存一致性错误。为了避免副作用,您必须使用同步变量。但是在java中有一个更好的解决方案。

使用简单的原子变量访问比通过同步代码访问这些变量更有效

concurrent包中的一些类提供了不依赖于同步的原子方法。

有关更多细节,请参阅这篇高级并发控制文章。

尤其要看一下原子变量。

相关的SE问题:

挥发性Vs原子性

Volatile boolean vs AtomicBoolean

Java中volatile和synchronized的区别