Answers:
在Java中声明一个静态变量,意味着将只创建一个副本,而不管该类中创建了多少个对象。即使根本没有Objects
创建,也可以访问该变量。但是,线程可能具有其本地缓存的值。
当变量是易变的而不是静态的时,每个变量将只有一个Object
。因此,从表面上看,它与普通变量没有什么区别,但与static完全不同。但是,即使具有Object
字段,线程也可以在本地缓存变量值。
这意味着,如果两个线程同时更新同一对象的变量,并且该变量未声明为volatile,则可能存在其中一个线程在缓存中保留旧值的情况。
即使您通过多个线程访问静态值,每个线程也可以拥有其本地缓存副本!为避免这种情况,您可以将变量声明为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
)正在访问同一个对象并更新声明为volatile的变量,则意味着t1
并t2
可以对其对象进行自己的本地缓存,但声明为volatile的变量除外。因此,volatile变量将只有一个主副本,该副本将由不同的线程更新,并且一个线程对volatile变量所做的更新将立即反映到另一个线程。
volatile
变量也可以在不同的CPU缓存之间共享。这不会出现问题,因为高速缓存会在修改高速缓存行之前协商其独占所有权。
除了其他答案外,我还想为其添加一张图片(图片易于理解)
static
可以为单个线程缓存变量。在多线程环境中,如果一个线程修改了其缓存的数据,则可能无法反映其他线程,因为它们具有副本。
volatile
声明可确保线程不会缓存数据,而仅使用共享副本。
我认为static
并volatile
没有任何关系。我建议您阅读Java教程以了解Atomic Access,以及为什么要使用原子访问,了解交错的内容,您会找到答案。
简单来说,
使用易失性变量可降低内存一致性错误的风险,因为对易失性变量的任何写入都会与该变量的后续读取建立先发生后关系。这意味着对volatile变量的更改始终对其他线程可见
通过 更好地了解volatile变量来看看本文Javin Paul
。
在没有volatile
关键字的情况下,每个线程堆栈中变量的值可能不同。通过将变量设置为volatile
,所有线程将在其工作内存中获得相同的值,并且避免了内存一致性错误。
这里的术语variable
可以是static
(类)变量或instance
(对象)变量。
关于您的查询:
无论如何,静态变量值也将成为所有线程的一个值,那么为什么我们要使用volatile?
如果我instance
在应用程序中需要变量,则不能使用static
变量。即使在static
变量的情况下,由于如图所示的线程缓存,也不能保证一致性。
使用volatile
变量可降低内存一致性错误的风险,因为对易失性变量的任何写入都会与该变量的后续读取建立先发生后关系。这意味着对volatile变量的更改始终对其他线程可见。
而且,这还意味着当线程读取一个volatile变量时,它不仅看到volatile的最新更改,而且还看到导致更改的代码的副作用=> volatile变量仍然可能导致内存一致性错误。为避免副作用,您必须使用同步变量。但是在Java中有更好的解决方案。
使用简单的原子变量访问比通过同步代码访问这些变量更有效。
java.util.concurrent
软件包中的某些类提供了不依赖同步的原子方法。
有关更多详细信息,请参阅此高级并发控制文章。
尤其要看一下原子变量。
相关的SE问题:
volatile
较早的,但是,这个答案澄清了很多关于我为什么我仍然需要使用volatile
与static
变量。
如果我们将变量声明为静态变量,则该变量将只有一个副本。因此,每当不同线程访问该变量时,该变量将只有一个最终值(因为只有一个内存位置分配给该变量)。
如果将一个变量声明为volatile,则所有线程将拥有其自己的变量副本,但其值是从主内存中获取的,因此,所有线程中的变量值将相同。
因此,在两种情况下,主要要点是变量的值在所有线程中都相同。