本文是做者原創,版權歸做者全部.若要轉載,請註明出處.本文只貼我以爲比較重要的源碼
指令重排序
public static void main(String[] args) throws InterruptedException { int j=0; int k=0; j++; System.out.println(k); System.out.println(j); }
上面這段代碼可能會被重排序:以下java
public static void main(String[] args) throws InterruptedException { int k=0; System.out.println(k); int j=0; j++; System.out.println(j); }
此時指令的執行順序能夠與代碼邏輯順序不一致,但不影響程序的最終結果.linux
再看個demojvm
public class ThreadExample2 { static int i; public static boolean runing = true; public static void main(String[] args) throws InterruptedException { traditional(); Thread.sleep(100); runing = false; } public static void traditional() { Thread thread = new Thread() { @Override public void run() { while (runing){ i++;//沒有方法,JVM會作指令重排序,激進優化 } } }; thread.start(); } }
執行下main方法ide
能夠看出該程序一直在跑,不會中止.函數
此時jvm發現traditional方法內沒有其餘方法,JVM會作指令重排序,採起激進優化策略,對咱們的代碼進行了重排序性能
以下:測試
static int i; public static boolean runing = true; public static void main(String[] args) throws InterruptedException { traditional(); Thread.sleep(100); runing = false; } public static void traditional() { Thread thread = new Thread() { boolean temp=runing;//注意這裏,此時while的條件永遠爲true @Override public void run() { while (temp){ i++;//沒有方法,JVM會作指令重排序,激進優化 } } }; thread.start(); }
所以程序不會中止.優化
咱們稍微改動下代碼,在while 循環里加個方法spa
static int i; public static boolean runing = true; public static void main(String[] args) throws InterruptedException { traditional(); Thread.sleep(100); runing = false; } public static void traditional() { boolean temp=runing; Thread thread = new Thread() { @Override public void run() { while (runing){// i++;//沒有方法,JVM會作指令重排序,激進優化 //有方法,JVM認爲可能存在方法溢出,不作指令重排序,保守優化策略 aa(); } } }; thread.start(); } public static void aa(){ System.out.println("hello"); }
看下結果操作系統
能夠看出,程序自行中止了,由於有方法,JVM認爲可能存在方法溢出,不作指令重排序,採起保守優化策略
runing = false;
全局變量runing 改動值之後,被thread線程識別,while 循環裏值變爲false,就自動中止了.
ok,繼續,咱們把main方法中的sleep()註釋掉,以下
public static void main(String[] args) throws InterruptedException { traditional(); //Thread.sleep(100); runing = false;//會優先執行主線程的代碼 } public static void traditional() { boolean temp=runing; Thread thread = new Thread() { @Override public void run() { while (runing){// i++; } } }; thread.start(); }
看下結果:
此時,程序中止了,這是爲何呢:
多是由於thread 線程和main線程競爭cpu資源的時候,會優先分配給main線程(我不肯定,讀者們能夠本身思考一下)
Java 中的鎖
synchronized關鍵字
在1.6版本以前,synchronized都是重量級鎖
1.6以後,synchronized被優化,由於互斥鎖比較笨重,若是線程沒有互斥,那就不須要互斥鎖
重量級鎖
1.當一個線程要訪問一個共享變量時,先用鎖把變量鎖住,而後再操做,操做完了以後再釋放掉鎖,完成
2.當另外一個線程也要訪問這個變量時,發現這個變量被鎖住了,沒法訪問,它就會一直等待,直到鎖沒了,它再給這個變量上個鎖,而後使用,使用完了釋放鎖,以此進行
3.咱們能夠這麼理解:重量級鎖是調用操做系統的函數來實現的鎖--mutex--互斥鎖
以linux爲例:
1.互斥變量使用特定的數據類型:pthread_mutex_t結構體,能夠認爲這是一個函數 2.能夠用pthread_mutex_init進行函數動態的建立 : int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t * attr) 3.對鎖的操做主要包括加鎖 pthread_mutex_lock()、解鎖pthread_mutex_unlock()和測試加鎖 pthread_mutex_trylock()三個
3.1 int pthread_mutex_tlock(pthread_mutex_t *mutex) 在寄存器中對變量操做(加/減1) 3.2 int pthread_mutex_unlock(pthread_mutex_t *mutex) 釋放鎖,狀態恢復 3.3 int pthread_mutex_trylock(pthread_mutex_t *mutex) pthread_mutex_trylock()語義與pthread_mutex_lock()相似,不一樣的是在鎖已經被佔據時返回EBUSY而不是掛起等待
函數pthread_mutex_trylock會嘗試對互斥量加鎖,若是該互斥量已經被鎖住,函數調用失敗,返回EBUSY,不然加鎖成功返回0,線程不會被阻塞
偏向鎖
偏向鎖是synchronized鎖的對象沒有資源競爭的狀況下存在的,不會一直調用操做系統函數實現(第一次會調用),而重量級鎖每次都會調用
看個demo
public class SyncDemo2 { Object o= new Object(); public static void main(String[] args) { System.out.println("pppppppppppppppppppppp"); SyncDemo2 syncDemo = new SyncDemo2(); syncDemo.start(); } public void start() { Thread thread = new Thread() { public void run() { while (true) { try { Thread.sleep(500); sync(); } catch (InterruptedException e) { } } } }; Thread thread2 = new Thread() { @Override public void run() { while (true) { try { Thread.sleep(500); sync(); } catch (InterruptedException e) { e.printStackTrace(); } } } }; thread.setName("t1"); thread2.setName("t2"); //兩個線程競爭時,synchronized是重量級鎖,一個線程時,synchronized是偏向鎖 thread.start(); thread2.start(); } //在1.6版本以前,synchronized都是重量級鎖 //1.6以後,synchronized被優化,由於互斥鎖比較笨重,若是線程沒有互斥,那就不須要互斥鎖 public void sync() { synchronized (o) { System.out.println(Thread.currentThread().getName()); } } }
代碼很簡單,就是啓動兩個線程,而且調用同一個同步方法,看下結果
能夠看到,兩個線程都執行了該同步方法,此時兩個線程競爭,synchronized是重量級鎖
咱們把一個線程註釋掉
//兩個線程競爭時,synchronized是重量級鎖,一個線程時,synchronized是偏向鎖 thread.start(); //thread2.start();
看下結果:
此時synchronized是偏向鎖
那麼怎麼證實呢:我目前沒那個實力,給個思路.
1.須要編譯並修改linux源碼函數pthread_mutex_lock(),在函數中打印當前線程的pid
2.在同步方法中打印語句"current id"+當前pid(須要本身寫c語言實現),java的Thread.currentThread().getId()不能獲取操做系統級別的pid
3.兩個線程競爭時,執行一次
說明是重量級鎖,由於每次都調用操做系統的函數pthread_mutex_lock()來實現
4.註釋掉一個線程,再執行一次
說明是偏向鎖,由於第一次會調用pthread_mutex_lock(),後面就不調用系統函數了.