系列一講解了多線程,本章講解多線程開發中經常使用到的3個關鍵字synchronized、ReentrantLock、volatile。
一、synchronized
互斥鎖,即操作互斥,並發線程過來,串行獲得鎖,串行執行代碼。就像一個房間一把鑰匙,一個人進去后,下一個人得等第一個人出來得到鑰匙才能進入。如果代碼寫的不好(A),可能出現死鎖!(A得到鎖,B等待A釋放鎖,A不釋放,B死鎖)
示例代碼:
//修飾靜態方法:類級別互斥(只要是房子此方法就互斥) public synchronized static void foo1(){ //do } //修飾普通方法:對象級別互斥(只有同一間房子此方法互斥) public synchronized void foo2(){ //do } //修飾代碼塊 public void foo3(){ //類級別互斥 synchronized(DennyTest.class) { //do } } //修飾代碼塊 public void foo4(){ //對象級別互斥 synchronized(this) { //do } }
需要注意的是,經常使用的是對象級別的互斥,那么特別需要注意是同一個對象的鎖。新手經常犯錯,都不是同一個對象,當然鎖不住。
二、ReentrantLock
可重入鎖,和同步鎖功能類似,不過需要顯示的創建、銷毀。特點:
1.ReentrantLock有tryLock方法,如果鎖被其他線程持有,返回false,可避免形成死鎖。
2.創建時可自定義是否可搶占。
3.ReentrantReadWriteLock,用於讀多寫少,且讀不需要互斥的場景,大大提高性能。
示例代碼:
1、嘗試獲取一次 ReentrantLock lock = new ReentrantLock(); if (lock.tryLock()) { //得到執行,得不到不執行,就一次。 try { //操作 } finally { lock.unlock(); } } 2、同步執行,類似synchronized(也是使用最多的) ReentrantLock lock = new ReentrantLock(); //參數默認false,不公平鎖:可搶占 ReentrantLock lock = new ReentrantLock(true); //公平鎖:嚴格按照請求鎖的排隊順序獲取鎖 lock.lock(); //如果被其它資源鎖定,會在此等待鎖釋放,阻塞 try { //操作 } finally { lock.unlock(); } 3、嘗試等待固定時間再次獲取 ReentrantLock lock = new ReentrantLock(true); //公平鎖 try { if (lock.tryLock(5, TimeUnit.SECONDS)) { //如果已經被lock,嘗試等待5s,看是否可以獲得鎖,如果5s后仍然無法獲得鎖則返回false try { //操作 } finally { lock.unlock(); } } } catch (InterruptedException e) { e.printStackTrace(); //當前線程被中斷時(interrupt),會拋InterruptedException } 4、可中斷鎖的同步執行 ReentrantLock lock = new ReentrantLock(true); //公平鎖 lock.lockInterruptibly(); try { //操作 } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); }
三、volatile
volatile,翻譯過來是易變的。只保證同一變量在多線程中的可見性。
1)它確保指令重排序時不會把其后面的指令排到內存屏障之前的位置,也不會把前面的指令排到內存屏障的后面;即在執行到內存屏障這句指令時,在它前面的操作已經全部完成;
2)它會強制將對緩存的修改操作立即寫入主存;
3)如果是寫操作,它會導致其他CPU中對應的緩存行無效。
注意:雖然保證變量是主存數據,但是操作不是原子的,多線程讀取到同一個值(是主存的值),同時進行判斷或者操作,導致出錯。
總結:
可見,synchronized和ReentrantLock是一個級別的,但是volatile只是一個輕量級的關鍵字。可用場景:
1.狀態標記
1 volatile boolean inited = false; 2 //線程1: 3 context = loadContext(); 4 inited = true; 5 6 //線程2: 7 while(!inited ){ 8 sleep() 9 } 10 doSomethingwithconfig(context);
2.double check :使用 volatile 關鍵字來保證多線程下的單例
public class Singleton { private volatile Singleton instance = null; public Singleton getInstance() { if (instance == null) { synchronized(this) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
參考資料:
《深入理解Java虛擬機》
《effective java》
http://www.cnblogs.com/dolphin0520/p/3920373.html