Synchronize原理


1 普通方法上

2 靜態方法上

 

 修飾靜態方法內置鎖是當前的Class字節碼對象

 修飾普通方法內置鎖是當前類的實例

 

 

原理與使用:

   從字節碼層面解釋:

     執行同步代碼塊

     monitorenter 

     synchronized( ){

 

 

     }

   monitorexit  

 

任何對象都可以作為鎖,那么鎖信息有存在對象的什么地方呢? 

   存在對象頭中

對象頭中的信息    Mark Word   Class Metadata Address   ArrayLength

Mark Word 存儲的是哈希值 鎖信息  空間利用率很高的

 

任何對象都可以作為鎖,那么鎖信息又存在對象的什么地方呢? 

   存在對象頭中

      對象頭中的信息: Mark Word             Class MetaData Address    

      

jdk1.6之后 引入了

偏向鎖:  每次獲取鎖和釋放鎖會浪費資源。很多情況下,競爭者不是由多個線程,而是由一個線程在使用。

                 

  當一個線程進來時候,找對象頭查看是否是偏向鎖。偏向鎖的Mark Word  字段會記錄一些信息 :線程id    Epoch  對象分代年齡信息  是否是偏向鎖    鎖標志位  

  檢查鎖標志位是否是偏向鎖,接着會檢查線程的id如果和當前線程id是一致(第一次直接進來)。第二次進來時候 接着運行同步代碼塊(沒有鎖的獲取和釋放)。偏向鎖獲取時候,沒有撤銷。等到有競爭時候 才有釋放的機制。  場景:只有一個線程訪問代碼塊會大大提高性能。(多個線程會降低性能)

輕量級鎖:

    首先棧幀

      虛擬機棧里面存儲的是一個個棧幀,每個方法執行都會存儲一個棧幀。每個方法執行都會執行棧幀的進棧出棧。

      復制Mark Word 到虛擬機棧中

      競爭成功之后會將鎖的標志位改成輕量級鎖,然后執行同步體。  

     其他線程也是復制Mark Word到虛擬機中

      修改Mark Word,發現已經被別的線程獲得了鎖,所以修改不成功。然后不停的修改。直到第一個線程把鎖釋放了才OK

      第一個線程執行完畢,第二個獲取鎖之后,會升級為重量級鎖(普遍意義上的synchronize),線程會阻塞。 
      

     輕量級鎖用到了自旋概念。好處:多個線程可以同時

     

 

關於單例模式結合Synchronize

      單例模式與線程安全問題

         餓漢式是沒有線程安全問題的

         懶漢式是有線程安全問題的

 

    縮小同步代碼塊的范圍,只有在創建時候才有寫的操作 其他的都是讀的操作沒有線程安全問題

     在會出現線程安全問題地方加一個synchronize,鎖當前的代碼 包裹起來

     這樣其實還是有問題的,雙重加鎖,再來一個驗空。

     這樣視乎是一個沒有問題,重排序問題。

     有可能先執行下面的 在執行上面的這樣的重排序問題(沒法演示)

     

   

package com.toov5.threadTest;
public class DoubleLock {

    private static volatile DoubleLock doubleLock;   //防止重排序
    
    private DoubleLock(){
        
    }
    public static DoubleLock getInstance(){  //比較餓漢模式而言的,如果在這一行加鎖,效率勢必會比較低  利用同步代碼塊
        //自旋while(true)
        if (doubleLock == null){   //判斷一下  如果為null時候  這種情況下才會去創建對象
            synchronized (DoubleLock.class){ //靜態的用 字節碼文件    
                if (doubleLock==null) {  //上鎖完了之后 可能沒有創建完畢 所以要判斷一下  但是 重排序時候 可能會導致 對象可能創建多次
                                         //所以加volitile關鍵字 (單線程是不會產生重排序問題)
                    doubleLock=new DoubleLock();
                }
            }

        }
        return doubleLock;   //不為空的話 會直接返回呀  多個線程去創建時候 才有有安全塞問題
    }
    
    
    
    public static void main(String[] args) {
         DoubleLock doubleLock1 = DoubleLock.getInstance();
         DoubleLock doubleLock2 = DoubleLock.getInstance();
         System.out.println(doubleLock1==doubleLock2);
    }
    
}

 

 

 

   

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM