我有一个多线程应用程序,其中线程使用全局变量。此变量仅在主线程中更新一次。
确保所有其他线程在更新时采用最新值的最佳方法是什么?
将此变量定义为volatile会影响性能,因为我需要在几天内更新一次。
答案 0 :(得分:0)
将变量声明为volatile
,您就完成了。
...但确实可以在某些情况下优化对变量的访问。您希望100%确定没有其他变量与您的变量共享缓存行。为什么?因为如果不发生共享,则在 S (共享)状态的所有核心的CPU缓存中都可以复制最新版本的变量。读取此变量很快就好像它没有声明为volatile
。但是如果有其他数据共享缓存行与您的变量和其他数据被频繁修改(例如每微秒),比希望读取您的变量的线程被阻止,而您的变量的最新值从CPU的缓存转移另一项数据已更新。
为了演示上述场景,这里是程序:
import java.util.concurrent.TimeUnit;
public class CacheLineSharing {
static class Holder {
volatile long pad0;
volatile long pad1;
volatile long pad2;
volatile long pad3;
volatile long pad4;
volatile long pad5;
volatile long pad6; // either pad6 or pad7 shares cache line with x
volatile long x = INIT_VALUE;
volatile long pad7;
volatile long pad8;
volatile long pad9;
volatile long pad10;
volatile long pad11;
volatile long pad12;
volatile long pad13;
volatile long pad14; // definitely in different cache line than x
}
static final int INIT_VALUE = 0;
static final int START_VALUE = 1;
static final int FINISH_VALUE = 2;
static class MyThread extends Thread {
Holder holder;
MyThread(Holder holder) {
this.holder = holder;
}
@Override
public void run() {
while (holder.x != START_VALUE);
long cycles = 0;
while (holder.x != FINISH_VALUE) {
cycles++;
}
System.out.println(String.format("cycles=%d", cycles));
}
}
public static void main(String[] args) throws Exception {
Holder holder = new Holder();
for (int i = 0; i < 4; i++) {
new MyThread(holder).start();
}
long ts = System.nanoTime();
holder.x = START_VALUE;
for (int i = 0; i < 1000000000; i++) {
//holder.pad6 = i;
holder.pad14 = i;
}
holder.x = FINISH_VALUE;
long nanoes = System.nanoTime() - ts;
System.out.println(String.format("millis=%d", TimeUnit.NANOSECONDS.toMillis(nanoes)));
}
}
我们的目标变量是x
,我们测量读取其值的速度。在第一种情况下,我们不断更新pad6
变量(它有7次机会与x
共享缓存行):
millis=4317
cycles=584637722
cycles=655006968
cycles=1214910177
cycles=1133123641
最快线程每秒~280K迭代次数
现在相同的程序更新pad14
变量,该变量不与x
共享缓存行(现代Intel CPU的缓存行为64字节= 8个java长):
cycles=1857323945
cycles=2034309531
cycles=1820202891
millis=1363
cycles=2083430201
即使是最慢的线程,〜每秒1335K次迭代。
快4.75倍!
所以,如果你对优化感到非常生气,那么在变量7之前填充你的变量并且在7之后填充变量。