当咱们在进行多线程编程时,若是线程之间不须要共享变量,可是各个线程操做的变量处于同一个cache line时,就会发生“伪共享”现象。这种现象并不会形成程序错误,而是若是程序员不注意这个问题会无形下降程序效率。java
cache line又称做缓存行,是cpu加载数据的一个基本单位,就比如磁盘加载数据的基本单位是一个扇区,windows文件系统加载数据的基本单位是簇。这样设计的主要目的在于一次性尽可能多加载数据避免重复从内存读取,进而提升效率。程序员
废话很少说,先看代码:编程
public class FalseSharingTest {windows
private static class FalseShareObject {
long p1, p2, p3, p4, p5, p6;
volatile long value;
}缓存
public static void main(String[] args) throws Exception {
FalseShareObject[] fsos = new FalseShareObject[5];
for (int i = 0; i < fsos.length; i++) {
fsos[i] = new FalseShareObject();
}
Thread[] threads = new Thread[fsos.length];
for (int i = 0; i < fsos.length; i++) {
final int idx = i;
threads[idx] = new Thread() {
public void run() {
for (long j = 0; j < 100000000l; j++) {
fsos[idx].value = j;
}
}
};
}多线程
for (Thread t : threads) {
t.start();
}操作系统
long start = System.currentTimeMillis();
for (Thread t : threads) {
t.join();
}
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start));
}线程
}
运行耗时:1667毫秒设计
当注释掉// long p1, p2, p3, p4, p5, p6; 后并无改变程序的语义,运行耗时:5169毫秒指针
运行的结果代表伪共享的现象是确实存在的。
该程序须要注意的是FalseShareObject对象中的value变量用volatile修饰能够看出明显的现象,若是去掉后则不明显。由于根据mesi缓存一致性协议,只有当缓存行的数据与内存数据一致时,处于同一个缓存行的数据才能被其它内核共享,进而使得被共享的缓存行数据在其它内核中产生cache line无效(也就是所谓的cache miss),其他的线程则频繁的从内存读取数据到cache line中从而下降效率,从而发生了所谓的“伪共享”现象。
为何要定义 long p1, p2, p3, p4, p5, p6这几个变量呢,主要是由于cache line通常大小为64个字节,而java对象头在64位操做系统中不开启指针压缩占用16个字节,这样加上6个long类型的变量后基本能够确保变量value占用独立的缓存行。
代码下载连接:https://pan.baidu.com/s/16_olDOQqXw612fft6oTGCQ 密码:i2x3