静态意味着所有对象的值的一个副本,而volatile意味着所有线程的值的一个副本,这是正确的吗?
不管怎样,一个静态变量值对于所有线程来说都是一个值,那为什么我们要用volatile呢?
静态意味着所有对象的值的一个副本,而volatile意味着所有线程的值的一个副本,这是正确的吗?
不管怎样,一个静态变量值对于所有线程来说都是一个值,那为什么我们要用volatile呢?
当前回答
说静态意味着所有对象的值都有一个副本是不正确的,因为静态意味着加载包含类的每个类加载器只有一个副本。
Java的volatile关键字意味着,对volatile变量的每一次读取都将从计算机的主存中读取,而不是从CPU缓存中读取,对volatile变量的每一次写入都将写入主存,而不仅仅是写入CPU缓存中。
其他回答
易失性变量值的访问将直接从主存储器。它应该只在多线程环境中使用。 静态变量将被加载一次。如果它在单线程环境中使用,即使变量的副本将被更新,访问它也不会有任何伤害,因为只有一个线程。
现在,如果静态变量在多线程环境中使用,那么如果有人期望从它得到想要的结果,就会出现问题。由于每个线程都有自己的副本,因此一个线程对静态变量的任何增量或减量可能不会反映在另一个线程中。
如果一个人期望从静态变量得到想要的结果,那么在多线程中使用volatile和static,那么一切都将得到解决。
如果将变量声明为静态,则该变量将只有一个副本。 因此,每当不同的线程访问该变量时,该变量将只有一个最终值(因为仅为该变量分配了一个内存位置)。
如果一个变量被声明为volatile,那么所有线程都将有自己的变量副本,但是值是从主存中取出的。因此,变量在所有线程中的值都是相同的。
因此,在这两种情况下,重点是变量的值在所有线程中是相同的。
不确定静态变量是否缓存在线程本地内存中。但是当我执行两个线程(T1,T2)访问同一个对象(obj)时,当T1线程对静态变量进行更新时,它在T2中得到了反映。
简单来说,
静态:静态变量与类相关联,而不是与任何对象相关联。类的每个实例共享一个类变量,该变量位于内存中的一个固定位置 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的区别
在Java中声明一个静态变量,意味着无论创建了多少个类的对象,都将只有一个副本。即使没有创建任何对象,也可以访问该变量。但是,线程可以在本地缓存它的值。
当一个变量是volatile而不是static时,每个Object都有一个变量。因此,从表面上看,它似乎与正常变量没有区别,但与静态变量完全不同。然而,即使使用Object字段,线程也可以在本地缓存变量值。
这意味着如果两个线程同时更新同一个Object的变量,并且变量没有被声明为volatile,则可能会出现其中一个线程缓存了旧值的情况。
即使您通过多个线程访问静态值,每个线程也可以有其本地缓存副本!为了避免这种情况,你可以将变量声明为static volatile,这将强制线程每次读取全局值。
然而,volatile并不能代替正确的同步! 例如:
private static volatile int counter = 0;
private void concurrentMethodWrong() {
counter = counter + 5;
//do something
counter = counter - 5;
}
多次并发执行concurrentMethodWrong可能导致counter的最终值不为零! 要解决这个问题,你必须实现一个锁:
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类。