java多線程開發中,控制共享數據比較麻煩,有可見性和同步性。通常控制可見性咱們能夠經過synchronized和volatile控制,而同步性咱們只能經過synchronized或Lock來控制。java
我喜歡經過對一個問題的理解,來理解某個知識點,由於我以爲知識就是爲了解決問題。多線程
public class SynchronizedTest { private static boolean flag = true; public static void main(String[] args) throws Exception{ new Thread(new Runnable() { @Override public void run() { while(flag) { } System.out.println("子線程執行結束================"); } }).start(); Thread.sleep(1000); flag = false; // 關閉線程輸出 System.out.println("flag已被修改成false"); } }
上面的代碼在64位JVM機器(準確來講應該是jvm的server模式)上執行通常都不會輸出「子線程執行結束================」,而在32位的jvm的client模式下卻能夠輸出「子線程執行結束================」。jvm
經過 java -version 能夠查看本身本機jvm運行的模式。ide
注意:若是你的jvm裝的是64位,那麼是沒法切換到client模式的。優化
上面的問題引發的緣由其實就是因爲java的內存模型引發的,java內存模型中分有主內存和工做內存之分,主內存能夠理解爲共享數據的區域(不知道準確不許確),而工做內存(主內存數據的副本)是每一個線程私有的一塊區域,每一個線程對共享數據的修改,不會直接操做共享數據,通常是先修改工做內存中的數據,而後在某個特定的時候刷新到主存,其餘線程纔有機會看到其修改。this
解決以上問題線程
private static volatile boolean flag = true;code
緣由:能夠這麼簡單理解,加了volatile關鍵字的共享變量,全部線程對其值的獲取或修改都直接經過主存,繞過來工做內存,因此主線程對flag的修改子線程立刻就能夠看到。server
volatile的原理就是加內存屏障,全部的讀都在寫以後,這樣保證了子線程對flag的訪問在主線程對flag的修改以後。對象
new Thread(new Runnable() { @Override public void run() { int count = 0; while(flag) { synchronized(SynchronizedTest.class){ //這裏能夠鎖任何共享對象 count++; } } System.out.println("子線程執行結束================"); } }).start();
上面之因此加上 count ,是防止JIT優化將無用的鎖代碼塊優化掉。
緣由:因爲jvm規定在進入synchronized以前會將全部其餘線程的工做內存刷新到主存(這個我不太肯定是否正確,若是有肯定的請留言告訴我,謝謝),在離開synchronized塊以前會將本線程的工做內存刷新到主內存。
在驗證這個之中我踩過一個坑,個人代碼以下:
new Thread(new Runnable() { @Override public void run() { while(flag) { System.out.println("====falg====="); } System.out.println("子線程執行結束================"); } }).start();
這樣在任何狀況下均可以輸出"子線程執行結束================",找了好久才發現原來 System.out.println 方法中有synchronized同步塊致使。
這個方法來自 System.out
public void println(String x) { synchronized (this) { print(x); newLine(); } }
之中有很多觀點是我本身的觀點,可能不許確,或是錯誤的,但願大家能給我指出,謝謝。