Jot*_*thi 260 java concurrency static multithreading volatile
说这static意味着所有对象的值的volatile一个副本并且意味着所有线程的值的一个副本是否正确?
无论如何,static变量值也将成为所有线程的一个值,那么我们为什么要这样做volatile呢?
sti*_*vlo 358
在Java中声明一个静态变量意味着无论创建了多少个类的对象,都只有一个副本.即使没有Objects创建,也可以访问该变量.但是,线程可能具有本地缓存的值.
当变量是易失性而非静态变量时,每个变量都会有一个变量Object.因此,从表面上看,似乎与正常变量没有区别,但与静态变量完全不同.但是,即使使用Object字段,线程也可以在本地缓存变量值.
这意味着如果两个线程同时更新同一个Object的变量,并且该变量未声明为volatile,则可能存在其中一个线程在缓存中具有旧值的情况.
即使您通过多个线程访问静态值,每个线程也可以拥有其本地缓存副本!为了避免这种情况,您可以将变量声明为静态volatile,这将强制线程每次读取全局值.
但是,volatile不能代替正确的同步!
例如:
private static volatile int counter = 0;
private void concurrentMethodWrong() {
counter = counter + 5;
//do something
counter = counter - 5;
}
Run Code Online (Sandbox Code Playgroud)
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;
}
}
Run Code Online (Sandbox Code Playgroud)
或者使用AtomicInteger课程.
Som*_*Som 284
静态和易失性之间的区别:
静态变量:如果两个线程(假设t1和t2)访问相同的对象,并更新其声明为静态的话,那就意味着一个变量t1,并t2可以在其各自的缓存使得同一对象(包括静态变量)的自己的本地副本,所以更新通过提出t1在其本地缓存中的静态变量不会反映在静态变量t2缓存.
静态变量在Object的上下文中使用,其中由一个对象进行的更新将反映在同一个类的所有其他对象中但不在Thread的上下文中,其中一个线程更新为静态变量将立即将更改反映到所有线程(在其本地缓存中).
volatile变量:如果两个线程(假设t1和t2)访问相同的对象,并更新其声明为挥发性那么就意味着一个变量t1,并t2可以使对象的自己的本地缓存,除了被声明为易失性变量.因此,volatile变量只有一个主副本,它将由不同的线程更新,并且由一个线程对volatile变量进行的更新将立即反映到另一个Thread.
简单来说,
使用易失性变量可以降低内存一致性错误的风险,因为对易失性变量的任何写入都会与同一变量的后续读取建立先发生关系。这意味着对 volatile 变量的更改始终对其他线程可见
看看这篇文章,以Javin Paul 更好地理解易失性变量。
如果没有volatile关键字,每个线程堆栈中的变量值可能不同。通过将变量设置为volatile,所有线程将在其工作内存中获得相同的值,并且避免了内存一致性错误。
这里的术语variable可以是static(类)变量或instance(对象)变量。
关于您的查询:
无论如何,静态变量值也将是所有线程的一个值,那么为什么我们应该选择 volatile 呢?
如果instance我的应用程序需要变量,我就不能使用static变量。即使在变量的情况下static,由于线程缓存的原因,也不能保证一致性,如图所示。
使用volatile变量可以降低内存一致性错误的风险,因为对易失性变量的任何写入都会与同一变量的后续读取建立先发生关系。这意味着对易失性变量的更改始终对其他线程可见。
更重要的是,这也意味着当一个线程读取一个 volatile 变量时,它不仅看到了 volatile 的最新变化,而且还看到了导致变化的代码的副作用 => 易失性变量仍然可能出现内存一致性错误。为了避免副作用,必须使用同步变量。但java中有更好的解决方案。
使用简单的原子变量访问比通过同步代码访问这些变量更有效
包中的一些类java.util.concurrent提供不依赖于同步的原子方法。
有关更多详细信息,请参阅这篇高级并发控制文章。
特别是看看原子变量。
相关SE问题:
| 归档时间: |
|
| 查看次数: |
144309 次 |
| 最近记录: |