标签:idt rup top ide 产生 多级 table png vol
我们都知道volatile为共享变量提供了可见性,下面就来分析这种可见性是如何实现的。
在说volatile之前,首先需要引入一个概念:缓存行。为了增加cpu的访存速度,通常会在cpu和内存之间增加多级缓存,如下图,L1、L2都是核心独享的缓存,L3为单个插槽上所有cpu共享的缓存,MainMemory为所有cpu共享。
根据局部性原理,cpu每次访问主存时都会读取至少一个缓存行的数据(通常一个缓存行为64字节,哪怕读取4字节数据,也会连续读取该数据之后的60字节),这里省略了多级缓存。
下面来做一个测试,两个线程分别对一个共享int变量进行10000000次++操作,最终的输出大概率小于20000000且大于10000000,这说明JMM对共享变量的更新操作的逻辑是,先同步会主存,然后再从主存读取该共享变量。
1 private int i = 0; 2 @Test 3 public void testIPlusPlus() throws InterruptedException { 4 CountDownLatch countDownLatch = new CountDownLatch(2); 5 class Task implements Runnable { 6 7 @Override 8 public void run() { 9 for(int j=0; j<10000000; j++) { 10 i++; 11 } 12 countDownLatch.countDown(); 13 } 14 } 15 new Thread(new Task()).start(); 16 new Thread(new Task()).start(); 17 countDownLatch.await(); 18 System.out.println(i); 19 }
说了这么多,现在来看volatile。
Java代码: | instance = new Singleton();//instance是volatile变量 |
汇编代码: |
movb $0x0,0x1104800(%esi); lock addl $0x0,(%esp); |
lock指令会锁定共享变量所在的所有缓存行,变量更新完成同步回主存后再释放,这样就会产生一个性能问题,当一个线程更新变量时,其他线程都无法对该变量的相邻变量操作了(因为相邻变量被预读取在同一缓存行)。
造成这种情况的根本原因在于,volatile锁定了缓存行,故要想优化volatile,可以使用填充空白字节的方法,将多个无关的共享变量存放在多个缓存行中。但这样做并不都是有利的,因为填充空白字节使得对象膨胀。
参考:http://ifeve.com/volatile/
http://ifeve.com/disruptor-cacheline-padding/
标签:idt rup top ide 产生 多级 table png vol
原文地址:http://www.cnblogs.com/holoyong/p/7620542.html