1、背景html
最近在看<Java并发编程实战>这本书,看到共享变量的可见性,其中说到“加锁的含义不单单局限于互斥行为,还包括内存可见性”。java
我对于内存可见性第一反应是volatile:被volatile修饰的变量可以保证每一个线程可以获取该变量的最新值,从而避免出现数据脏读的现象。编程
缘由是volatile修饰的共享变量进行写操做的时候会多出Lock前缀的指令,经过多处理器的缓存一致性协议,来保持变量的同步更新。缓存
可是我却没明白“加锁”与“可见性”这句话表达的意思,仔细思考下确实是这样子的。并发
2、实践ide
1 public class NoSivibilityVariable { 2 private static boolean isOver = false; 3 public static void main(String[] args) { 4 Thread thread = new Thread(new Runnable() { 5 @Override 6 public void run() { 7 while (!isOver); 8 System.out.println("thread ----- true"); 9 } 10 }); 11 thread.start(); 12 try { 13 Thread.sleep(500); 14 } catch (InterruptedException e) { 15 e.printStackTrace(); 16 } 17 isOver = true; 18 System.out.println("main ----- true"); 19 } 20 }
执行上面这段代码,只会打印出“main ----- true”而后一直死循环阻塞。缘由是当主线程把变量isOver修改成true,值的修改做用范围仅仅是当前线程内(主线程)而另外的线程是主内存的值,并无读取主线程修改后的值,因此另外一个线程和主内存的值都是失效的值。spa
若是要解决这个状况怎么办?线程
1.常见的作法就是把第二行 private static boolean isOver = false修改成 private static volatile boolean isOver = false就不会出现这种状况。code
2.就像书中所说的经过加锁来解决。htm
1 package synchronized_word; 2 3 public class NoSivibilityVariable { 4 private static boolean isOver = false; 5 6 public static void main(String[] args) { 7 Thread thread = new Thread(new Runnable() { 8 @Override 9 public void run() { 10 synchronized (NoSivibilityVariable.class) { 11 while (!isOver); 12 System.out.println("thread ----- true"); 13 } 14 } 15 }); 16 thread.start(); 17 18 19 try { 20 Thread.sleep(500); 21 } catch (InterruptedException e) { 22 e.printStackTrace(); 23 } 24 synchronized (NoSivibilityVariable.class) { 25 isOver = true; 26 } 27 System.out.println("main ----- true"); 28 } 29 }
2个线程中在对共享变量的读取或者写入都进行加锁处理,由于线程对应的都是同一把锁对象(该类对象)因此相互会排斥。可是就算这样子也不能说明内存可见性的。其实真正解决这个问题的是JMM关于Synchronized的两条规定:
一、线程解锁前,必须把共享变量的最新值刷新到主内存中;
二、线程加锁时,讲清空工做内存中共享变量的值,从而使用共享变量是须要从主内存中从新读取最新的值(加锁与解锁须要统一把锁)
线程执行互斥锁代码的过程:
1.得到互斥锁
2.清空工做内存
3.从主内存拷贝最新变量副本到工做内存
4.执行代码块
5.将更改后的共享变量的值刷新到主内存中
6.释放互斥锁
http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#incorrectlySync
这里提到synchronized会保证对进入同一个监视器的线程保证可见性。好比线程 t1修改了变量,退出监视器以前,会把修改变量值v1刷新的主内存当中;当线程t2进入这个监视器时,若是有某个处理器缓存了变量v1,首先缓存失效,而后必须重主内存从新加载变量值v1(这点和volatile很像)。这里语义的解读只是说了对于同一个监视器,变量的可见性有必定的方式可寻,非同一个监视器就不保证了。
3、总结
synchronized具备内存可见性,为了确保全部线程可以看到共享变量的值是最新的,全部执行读操做或写操做的线程都必须在同一个锁上同步。