Java中的Volatile vs Static

这样说是否正确:static表示所有对象的值的一个副本,而volatile表示所有线程的值的一个副本?

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

168931 次浏览
如果将一个变量声明为static,则该变量将只有一个副本。 因此,无论何时不同的线程访问该变量,该变量将只有一个最终值(因为仅为该变量分配了一个内存位置)

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

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

在Java中声明静态变量意味着无论创建了多少个类的对象,都将只有一个副本。即使根本没有创建Objects,变量也可以访问。但是,线程可以在本地缓存它的值。

当一个变量是挥发性而不是静态时,每个Object将有一个变量。因此,从表面上看,它似乎与普通变量没有区别,但与静态完全不同。然而,即使使用Object字段,线程也可以在本地缓存变量值。

这意味着如果两个线程同时更新同一个Object的变量,并且变量没有被声明为volatile,则可能会出现其中一个线程缓存了旧值的情况。

即使你通过多个线程访问静态值,每个线程都可以有它的本地缓存副本!为了避免这种情况,你可以将变量声明为静态不稳定,这将强制线程每次读取全局值。

然而,挥发性并不能代替正确的同步!< br > 例如:< / p >
private static volatile int counter = 0;


private void concurrentMethodWrong() {
counter = counter + 5;
//do something
counter = counter - 5;
}
同时执行concurrentMethodWrong多次可能会导致counter的最终值不为零!< br > 为了解决这个问题,你必须实现一个锁:

private static final Object counterLock = new Object();


private static volatile int counter = 0;


private void concurrentMethodRight() {
synchronized (counterLock) {
counter = counter + 5;
}
//do something
synchronized (counterLock) {
counter = counter - 5;
}
}

或者使用AtomicInteger类。

Static和Volatile的区别:

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

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

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

我认为staticvolatile完全没有关系。我建议你阅读java教程来了解原子访问,以及为什么使用原子访问,了解什么是交叉,你就会找到答案。

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

enter image description here

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

volatile声明确保线程只不会缓存数据并使用共享副本

图像来源 .

简单来说,

  1. static: static变量与相关,而不是与任何对象相关。类的每个实例共享一个类变量,该变量位于内存中的一个固定位置

  2. volatile:该关键字同时适用于classinstance变量。

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

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

enter image description here

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

这里的术语variable可以是static(类)变量或instance(对象)变量。

关于你的问题:

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

如果在应用程序中需要instance变量,则不能使用static变量。即使在static变量的情况下,由于图中所示的线程缓存,一致性也不能保证。

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

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

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

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

更多细节请参考这篇高级并发控制文章。

特别是看一下原子变量

相关的SE问题:

Volatile Vs Atomic

Volatile boolean vs AtomicBoolean

Java中volatile和synchronized的区别 .

易失性变量值访问将直接从主存。它应该只在多线程环境中使用。 静态变量将被加载一次。如果它在单线程环境中使用,即使变量的副本将被更新,访问它也不会有任何伤害,因为只有一个线程

现在,如果静态变量在多线程环境中使用,那么如果有人期望从它得到想要的结果,就会出现问题。由于每个线程都有自己的副本,因此一个线程对静态变量的任何增量或减量可能不会反映在另一个线程中。

如果一个人期望从静态变量得到想要的结果,那么在多线程中使用volatile和static,那么一切都将得到解决。

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