java锁以及双重检查
雙檢鎖/雙重校驗鎖 雙層對空判斷困擾了很久。實例
public class Singleton {private volatile static Singleton singleton;//私有構造函數避免調用private Singleton (){}public static Singleton getSingleton() {// 先判斷對象是否創建過if (singleton == null) {//類對象加鎖synchronized (Singleton.class) {if (singleton == null) {singleton = new Singleton();//字節碼層//JIT CPU 可能對如下指令進行重排序// 1.分配空間// 2.初始化// 3.引用賦值如果指令重排后指令如下://1.分配空間//3.引用賦值 如果在當前指令執行完后,有其他線程獲取到實例,將拿到未初始化的實例;//2. 初始化}}}return singleton;} }
解釋:當A與B同時調用getSingleton時,判斷第一個if都為空,這時A拿到鎖,進行第二層if判斷,條件成立new了一個對象;
B在外層等待,A創建完成,釋放鎖,B拿到鎖,進行第二層if判斷,條件不成立,結束釋放鎖。C調用getSingleton時第一層判斷不成立,直接拿到singleton對象返回,避免進入鎖,減少性能開銷。
進一步理解:其中兩次判空,第一次判空是,減少多線程情況下,進入同步代碼塊的次數,第二次判空,是防止多線程(A,B兩種線程的情況下A,B 同時調用了 getSingleton 方法,都同時,進入到第一層if(singleton==null){} 內,競態條件下,如果A 拿到了對象鎖,進入到同步代碼塊,B阻塞等待,等待創建了實例對象,釋放了鎖后,B進入同步代碼塊,但是此時第二層if(singleton==null){} 判斷,singleton 不為空,直接返回singleton);
總結:其中有兩次判斷是否為空的語句,第一次是為了提高效率,避免每次都要執行同步代碼塊,第二次判空,是為了避免多線程帶來的不安全,當兩個線程同時對第一個判斷為空時,均會先后進入同步代碼塊,此時,若沒有第二個判空條件,則會引來創建多個實例。
volite 關鍵字:
采用volatile關鍵字修飾很有必要
這句代碼事實上是分三步:
singleton?= new Singleton();
但是,jvm會有指令重排的特性,執行順序有可能改變,不是按照123的順序,可能是132,這樣就會導致一個線程獲得沒有初始化的實例
如:t1執行了13,此時t2調用了getInstance()后發現singleton?已經不為空了,因此返回singleton?,但是這時singleton?還沒有被初始化
volatile就可以禁止jvm的指令重排,保證在多線程環境下也能正常運行
想進一步了解:推薦
深入理解Java并發之synchronized實現原理
https://blog.csdn.net/javazejian/article/details/72828483
?
參考:
https://blog.csdn.net/jlnu0812407/article/details/102836187
https://blog.csdn.net/faye_1008/article/details/90296173
總結
以上是生活随笔為你收集整理的java锁以及双重检查的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MobaXterm_Personal_2
- 下一篇: asp.net mvc使用mysql_A