说static
意味着一个值为所有对象提供一个副本,而volatile
意味着一个值为所有线程提供一个副本,这种说法正确吗?
不管怎样,static
变量的值也会为所有线程提供一个值,那么为什么我们要使用volatile
?
说static
意味着一个值为所有对象提供一个副本,而volatile
意味着一个值为所有线程提供一个副本,这种说法正确吗?
不管怎样,static
变量的值也会为所有线程提供一个值,那么为什么我们要使用volatile
?
volatile
而不是static
时,每个对象都会有一个变量。因此,表面上似乎与普通变量没有区别,但与static
完全不同。然而,即使是Object
字段,线程也可能在本地缓存变量值。volatile
,那么就可能存在一种情况,其中一个线程在缓存中具有旧值。static
值,每个线程也可以拥有自己的本地缓存副本!为了避免这种情况,可以将变量声明为static volatile
,这将强制线程每次读取全局值。volatile
不能替代适当的同步!例如:private static volatile int counter = 0;
private void concurrentMethodWrong() {
counter = counter + 5;
//do something
counter = counter - 5;
}
并发地多次执行concurrentMethodWrong
可能会导致计数器的最终值与零不同!
为了解决这个问题,你需要实现一个锁:
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
类。
静态变量和易失性变量的区别 :
静态变量: 如果两个线程(假设是t1
和t2
)正在访问同一个对象并更新一个被声明为静态的变量,那么就意味着t1
和t2
可以在各自的缓存中创建相同对象的本地副本(包括静态变量)。因此,t1
对其本地缓存中静态变量所做的更新不会反映在t2
缓存中的静态变量中。
静态变量用于对象上下文,其中一个对象的更新将反映在同一类的所有其他对象中,但在线程上下文中不会如此。其中一个线程对静态变量的更新将立即反映在所有线程的本地缓存中。
易失性变量: 如果两个线程(假设是t1
和t2
)正在访问同一个对象并更新一个被声明为易失性的变量,则意味着t1
和t2
可以在各自的缓存中创建对象的本地副本,但是被声明为易失性的变量将只有一个主要副本,不会被缓存。因此,不同线程对易失性变量所做的更新将立即反映到另一个线程。
volatile
变量也可以在不同的CPU缓存之间共享。这并不会造成问题,因为缓存在修改缓存行之前会协商独占所有权。 - David Schwartz除了其他答案,我想添加一张图以便理解:
static
变量可能会被缓存到每个线程中。在多线程环境下,如果一个线程修改了它的缓存数据,那么其他线程可能无法反映出这种变化,因为它们只有一份副本。
volatile
声明确保线程仅使用 共享的副本而不是缓存数据。
简单来说,
使用 volatile 变量可以降低内存一致性错误的风险,因为对 volatile 变量的任何写入都会与该变量的后续读取建立 happens-before 关系。这意味着对 volatile 变量的更改始终可见于其他线程。
请查看Javin Paul
的文章,以更好地理解 volatile 变量。
volatile
关键字的情况下,每个线程堆栈中变量的值可能不同。通过将变量设置为volatile
,所有线程都将获得相同的工作内存中的值,并且避免了内存一致性错误。variable
可以是静态(类)变量或实例(对象)变量。instance
变量,我不能使用静态变量。即使在静态变量的情况下,由于线程缓存,一致性也无法保证,如图所示。volatile
变量可以降低内存一致性错误的风险,因为对volatile
变量的任何写操作都会与该变量的后续读取建立happens-before关系。这意味着对volatile
变量的更改始终对其他线程可见。java.util.concurrent
包中的某些类提供了不依赖于同步的原子方法。volatile
,但是这个答案为我澄清了很多问题,为什么我仍然需要在static
变量中使用volatile
。 - Arefe错误的说法是静态意味着所有对象只有一个值的副本,因为静态意味着每个加载包含类的类加载器只有一个副本。
Java中的volatile关键字意味着每次读取volatile变量都将从计算机的主内存中读取,而不是从CPU缓存中读取,并且每次写入volatile变量都将写入主内存,而不仅仅是写入CPU缓存。
不确定静态变量是否缓存在线程本地内存中。但是当我执行两个访问同一对象(obj)的线程(T1,T2),并且当T1线程对静态变量进行更新时,它会在T2中反映出来。
volatile变量的值访问将直接从主内存中进行。它应该仅在多线程环境中使用。
静态变量将被加载一次。如果它在单线程环境中使用,即使变量的副本被更新,访问它也不会有任何影响,因为只有一个线程。
现在,如果静态变量在多线程环境中使用,则如果期望从中获得所需的结果,则会出现问题。由于每个线程都有自己的副本,因此来自一个线程的静态变量的任何增量或减量可能不会反映在另一个线程中。
如果希望从静态变量中获得所需的结果,则在多线程中使用volatile和static,然后一切都将得到解决。