Java 中的易失性与静态

说这意味着所有对象的值的一个副本,意味着所有线程的值的一个副本,这是否正确?staticvolatile

无论如何,变量值也将是所有线程的一个值,那么我们为什么要去呢?staticvolatile


答案 1

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

当变量是易失性的而不是静态的时,每个变量都会有一个变量。因此,从表面上看,似乎与正常变量没有区别,但与静态变量完全不同。但是,即使使用字段,线程也可能在本地缓存变量值。ObjectObject

这意味着,如果两个线程同时更新同一对象的变量,并且该变量未声明为易失性,则可能存在其中一个线程在缓存中具有旧值的情况。

即使您通过多个线程访问静态值,每个线程也可以拥有其本地缓存副本!为了避免这种情况,您可以将变量声明为静态易失性变量,这将强制线程在每次全局值时读取。

但是,易失性不能替代适当的同步!
例如:

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 类。


答案 2

静态和易失性之间的区别:

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

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

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


推荐