您好,登錄后才能下訂單哦!
這篇文章主要講解了“java中鎖的優化方法”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“java中鎖的優化方法”吧!
1)鎖消除
概念:JVM在JIT編譯(即時編譯)時,通過對運行上下文的掃描,去除掉那些不可能發生共享資源競爭的鎖,從而節省了線程請求這些鎖的時間。 舉例: StringBuffer的append方法是一個同步方法,如果StringBuffer類型的變量是一個局部變量,則該變量就不會被其它線程所使用,即對局部變量的操作是不會發生線程不安全的問題。 在這種情景下,JVM會在JIT編譯時自動將append方法上的鎖去掉。
2)鎖粗化
概念:將多個連續的加鎖、解鎖操作連接在一起,擴展成一個范圍更大的鎖,即將加鎖的粒度放大。 舉例:在for循環里的加鎖/解鎖操作,一般需要放到for循環外。
3)使用偏向鎖和輕量級鎖
說明: 1)java6為了減少獲取鎖和釋放鎖帶來的性能消耗,引入了偏向鎖和輕量級鎖。 2)鎖一共有4種狀態,級別從低到高依次是:無鎖狀態、偏向鎖、輕量級鎖、重量級鎖。 3)鎖的狀態會隨著競爭情況逐漸升級,并且只可以升級而不能降級。 【偏向鎖】 1)背景:大多數情況下,鎖不僅不存在多線程競爭,而且總是由同一線程多次獲得,為了讓線程獲得鎖的代價更低而引入了偏向鎖。 2)概念:核心思想就是鎖會偏向第一個獲取它的線程,如果在接下來的執行過程中沒有其它的線程獲取該鎖,則持有偏向鎖的線程永遠不需要同步。 3)目的:偏向鎖實際上是一種優化鎖,其目的是為了減少數據在無競爭情況下的性能損耗。 4)原理: 1>當一個線程訪問同步塊并獲取鎖時,會在對象頭和棧幀中的鎖記錄里存儲鎖偏向的線程ID。 2>以后該線程在進入和退出同步塊時就不需要進行CAS操作來加鎖和解鎖,只需簡單地判斷一下對象頭的Mark Word里是否存儲著指向當前線程的偏向鎖。 5)偏向鎖的獲取: 1>訪問Mark Word中偏向鎖的標識位是否為1,如果是1,則確定為偏向鎖。 說明: [1]如果偏向鎖的標識位為0,說明此時是處于無鎖狀態,則當前線程通過CAS操作嘗試獲取偏向鎖,如果獲取鎖成功,則將Mark Word中的偏向線程ID設置為當前線程ID;并且將偏向標識位設為1。 [2]如果偏向鎖的標識位不為1,也不為0(此時偏向鎖的標識位沒有值),說明發生了競爭,偏向鎖已經膨脹為輕量級鎖,這時使用CAS操作嘗試獲得鎖。 2>如果是偏向鎖,則判斷Mark Word中的偏向線程ID是否指向當前線程,如果偏向線程ID指向當前線程,則表明當前線程已經獲取到了鎖; 3>如果偏向線程ID并未指向當前線程,則通過CAS操作嘗試獲取偏向鎖,如果獲取鎖成功,則將Mark Word中的偏向線程ID設置為當前線程ID; 4>如果CAS獲取偏向鎖失敗,則表示有競爭。當到達全局安全點時(在這個時間點上沒有正在執行的字節碼),獲得偏向鎖的線程被掛起,偏向鎖升級為輕量級鎖,然后被阻塞在安全點的線程繼續往下執行同步代碼。 6)偏向鎖的釋放: 1>當其它的線程嘗試獲取偏向鎖時,持有偏向鎖的線程才會釋放偏向鎖。 2>釋放偏向鎖需要等待全局安全點(在這個時間點上沒有正在執行的字節碼)。 3>過程: 首先暫停擁有偏向鎖的線程,然后檢查持有偏向鎖的線程是否活著,如果線程不處于活動狀態,則將對象頭設置成無鎖狀態, 如果線程還活著,說明此時發生了競爭,則偏向鎖升級為輕量級鎖,然后剛剛被暫停的線程會繼續往下執行同步代碼。 7)優點:加鎖和解鎖不需要額外的消耗,和執行非同步方法相比僅存在納秒級的差距 8)缺點:如果線程間存在鎖競爭,鎖撤銷會帶來額外的消耗。 9)說明: 1)偏向鎖默認在應用程序啟動幾秒鐘之后才激活。 2)可以通過設置 -XX:BiasedLockingStartupDelay=0 來關閉延遲。 3)可以通過設置 -XX:-UseBiasedLocking=false 來關閉偏向鎖,程序默認會進入輕量級鎖狀態。(如果應用程序里的鎖大多情況下處于競爭狀態,則應該將偏向鎖關閉) 【輕量級鎖】 1)原理: 1>當使用輕量級鎖(鎖標識位為00)時,線程在執行同步塊之前,JVM會先在當前線程的棧楨中創建用于存儲鎖記錄的空間,并將對象頭中的Mark Word復制到鎖記錄中(注:鎖記錄中的標識字段稱為Displaced Mark Word)。 2>將對象頭中的MarkWord復制到棧楨中的鎖記錄中之后,虛擬機將嘗試使用CAS將對象頭中Mark Word替換為指向該線程虛擬機棧中鎖記錄的指針,此時如果沒有線程占有鎖或者沒有線程競爭鎖,則當前線程成功獲取到鎖,然后執行同步塊中的代碼。 3>如果在獲取到鎖的線程執行同步代碼的過程中,另一個線程也完成了棧楨中鎖記錄的創建,并且已經將對象頭中的MarkWord復制到了自己的鎖記錄中,然后嘗試使用CAS將對象頭中的MarkWord修改為指向自己的鎖記錄的指針,但是由于之前獲取到鎖的線程已經將對象頭中的MarkWord修改過了(并且現在還在執行同步體中的代碼,即仍然持有著鎖),所以此時對象頭中的MarkWord與當前線程鎖記錄中MarkWord的值不同,導致CAS操作失敗,然后該線程就會不停地循環使用CAS操作試圖將對象頭中的MarkWord替換為自己鎖記錄中MarkWord的值,(當循環次數或循環時間達到上限時停止循環)如果在循環結束之前CAS操作成功,那么該線程就可以成功獲取到鎖,如果循環結束之后依然獲取不到鎖,則鎖獲取失敗,對象頭中的MarkWord會被修改為指向重量級鎖的指針,然后這個獲取鎖失敗的線程就會被掛起,阻塞了。 4>當持有鎖的那個線程執行完同步體之后,使用CAS操作將對象頭中的MarkWord還原為最初的狀態時(將對象頭中指向鎖記錄的指針替換為Displaced Mark Word ),發現MarkWord已被修改為指向重量級鎖的指針,因此CAS操作失敗,該線程會釋放鎖并喚起阻塞等待的線程,開始新一輪奪鎖之爭,而此時,輕量級鎖已經膨脹為重量級鎖,所有競爭失敗的線程都會阻塞,而不是自旋。 自旋鎖: 1)所謂自旋鎖,就是讓沒有獲得鎖的進程自己運行一段時間自循環(默認開啟),但是不掛起線程。 2)自旋的代價就是該線程會一直占用處理器如果鎖占用的時間很短,自旋等待的效果很好,反之,自旋鎖會消耗大量處理器資源。 3)因此,自旋的等待時間必須有一定限度,超過限度還沒有獲得鎖,就要掛起線程。 優點:在沒有多線程競爭的前提下,減少傳統的重量級鎖帶來的性能損耗。 缺點:競爭的線程如果始終得不到鎖,自旋會消耗cpu。 應用:追求響應時間,同步塊執行速度非常快。 【重量級鎖】 說明: 1)java6之前的synchronized屬于重量級鎖,效率低下,因為monitor是依賴操作系統的Mutex Lock(互斥量)來實現的。 2)多線程競爭鎖時,會引起線程的上下文切換(即在cpu分配的時間片還沒有用完的情況下進行了上下文切換)。 3)操作系統實現線程的上下文切換需要從用戶態轉換到核心態,這個狀態之間的轉換需要相對較長的時間,時間成本相對較高。 4)在互斥狀態下,沒有得到鎖的線程會被掛起阻塞,而掛起線程和恢復線程的操作都需要從用戶態轉入內核態中完成。 優點:線程競爭不使用自旋,不會消耗cpu。 缺點:線程阻塞,響應時間緩慢。 應用:追求吞吐量,同步塊執行速度較長
感謝各位的閱讀,以上就是“java中鎖的優化方法”的內容了,經過本文的學習后,相信大家對java中鎖的優化方法這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。