Java線程并發(fā)中常見的鎖機制詳細介紹
隨著互聯(lián)網(wǎng)的蓬勃發(fā)展,越來越多的互聯(lián)網(wǎng)企業(yè)面臨著用戶量膨脹而帶來的并發(fā)安全問題。本文著重介紹了在java并發(fā)中常見的幾種鎖機制。
1.偏向鎖
偏向鎖是JDK1.6提出來的一種鎖優(yōu)化的機制。其核心的思想是,如果程序沒有競爭,則取消之前已經(jīng)取得鎖的線程同步操作。也就是說,若某一鎖被線程獲取后,便進入偏向模式,當線程再次請求這個鎖時,就無需再進行相關的同步操作了,從而節(jié)約了操作時間,如果在此之間有其他的線程進行了鎖請求,則鎖退出偏向模式。在JVM中使用-XX:+UseBiasedLocking
package jvmProject;
import java.util.List;
import java.util.Vector;
public class Biased {
public static List<Integer> numberList = new Vector<Integer>();
public static void main(String[] args) {
long begin = System.currentTimeMillis();
int count = 0;
int startnum = 0;
while(count<10000000){
numberList.add(startnum);
startnum+=2;
count++;
}
long end = System.currentTimeMillis();
System.out.println(end-begin);
}
}
初始化一個Vector,往里面添加10000000個Integer對象,然后輸出時間差。以此來測試偏向鎖的性能。至于為什么要使用Vector而不使用ArrayList呢?
因為ArrayList是線程不安全的,Vector是線程安全的。這樣說可能還不夠具體,可以翻看一下源碼吧。


Vector中的幾乎所有操作是帶有sychronized的,而ArrayList是沒有的,所以Vector是線程安全的。
接下來我們來測試一下,開啟偏向鎖和不開啟偏向鎖對程序性能的影響有多大。
配置JVM啟動(開啟偏向鎖)參數(shù)為:


配置JVM啟動(關閉偏向鎖)參數(shù)為:


Perfect!開啟偏向鎖的程序運行時間明顯較短,開啟偏向鎖比不開啟偏向鎖,在單個線程中操作一個對象的同步方法,是有一定的優(yōu)勢的。其實也可以這樣理解,當只有一個線程操作帶有同步方法的Vector對象的時候,此時對Vector的操作就轉變成了對ArrayList的操作。
偏向鎖在鎖競爭激烈的場合沒有太強的優(yōu)化效果,因為大量的競爭會導致持有鎖的線程不停地切換,鎖也很難保持在偏向模式,此時,使用偏向鎖不僅得不到性能的優(yōu)化,反而有可能降低系統(tǒng)的性能,因此,在激烈競爭的場合,可以嘗試使用
-XX:-UseBiastedLocking參數(shù)禁用偏向鎖。
2.輕量級鎖
如果偏向鎖失敗,Java虛擬機就會讓線程申請輕量級鎖,輕量級鎖在虛擬機內(nèi)部,使用一個成為BasicObjectLock的對象實現(xiàn)的,這個對象內(nèi)部由一個BasicLock對象和一個持有該鎖的Java對象指針組成。BasicObjectLock對象放置在Java棧幀中。在BasicLock對象內(nèi)部還維護著displaced_header字段,用于備份對象頭部的Mark Word.
當一個線程持有一個對象的鎖的時候,對象頭部Mark Word信息如下
[ptr |00] locked
末尾的兩位比特為00,整個Mark Word為指向BasicLock對象的指針。由于BasicObjectLock對象在線程棧中,因此該指針必然指向持有該鎖的線程??臻g。當需要判斷一個線程是否持有該對象時,只需要簡單地判斷對象頭的指針是否在當前線程的棧地址范圍即可。同時,BasicLock對象的displaced_header,備份了原對象的Mark word內(nèi)容,BasicObjectLock對象的obj字段則指向持有鎖的對象頭部。
3.重量級鎖
當輕量級鎖失敗,虛擬機就會使用重量級鎖。在使用重量級鎖的時,對象的Mark Word如下:
[ptr |10] monitor
重量級鎖在操作過程中,線程可能會被操作系統(tǒng)層面掛起,如果是這樣,線程間的切換和調用成本就會大大提高。
4.自旋鎖
自旋鎖可以使線程在沒有取得鎖的時候,不被掛起,而轉去執(zhí)行一個空循環(huán),(即所謂的自旋,就是自己執(zhí)行空循環(huán)),若在若干個空循環(huán)后,線程如果可以獲得鎖,則繼續(xù)執(zhí)行。若線程依然不能獲得鎖,才會被掛起。
使用自旋鎖后,線程被掛起的幾率相對減少,線程執(zhí)行的連貫性相對加強。因此,對于那些鎖競爭不是很激烈,鎖占用時間很短的并發(fā)線程,具有一定的積極意義,但對于鎖競爭激烈,單線程鎖占用很長時間的并發(fā)程序,自旋鎖在自旋等待后,往往毅然無法獲得對應的鎖,不僅僅白白浪費了CPU時間,最終還是免不了被掛起的操作 ,反而浪費了系統(tǒng)的資源。
在JDK1.6中,Java虛擬機提供-XX:+UseSpinning參數(shù)來開啟自旋鎖,使用-XX:PreBlockSpin參數(shù)來設置自旋鎖等待的次數(shù)。
在JDK1.7開始,自旋鎖的參數(shù)被取消,虛擬機不再支持由用戶配置自旋鎖,自旋鎖總是會執(zhí)行,自旋鎖次數(shù)也由虛擬機自動調整。
相關文章
圖文講解IDEA中根據(jù)數(shù)據(jù)庫自動生成實體類
這篇文章主要以圖文講解IDEA中根據(jù)數(shù)據(jù)庫自動生成實體類,本文主要以Mysql數(shù)據(jù)庫為例,應該會對大家有所幫助,如果有錯誤的地方,還望指正2023-03-03
autoMapping和autoMappingBehavior的區(qū)別及說明
這篇文章主要介紹了autoMapping和autoMappingBehavior的區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01
SpringBoot生產(chǎn)環(huán)境和測試環(huán)境配置分離的教程詳解
這篇文章主要介紹了SpringBoot生產(chǎn)環(huán)境和測試環(huán)境配置分離的教程詳解,需要的朋友可以參考下2020-08-08

