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); } }
