一、volatile
-
volatile的原理
在java中振坚,被volatile聲明的關(guān)鍵字,jvm會在翻譯的時候在cpu指令前加入lock前綴啃洋,而這個lock前綴的指令會在多核處理器下引發(fā)兩件事情呀狼。
- 將當(dāng)前處理器的緩存行的數(shù)據(jù)寫回到系統(tǒng)內(nèi)存。
- 這個寫回內(nèi)存的操作會使在其他cpu里緩存了該內(nèi)存地址的數(shù)據(jù)無效绝编,若其他線程還要讀取數(shù)據(jù)貌踏,則需要重新回到系統(tǒng)主存中取。
當(dāng)然volatile的使用是有條件的: - 對變量的寫操作不影響當(dāng)前值
- 使用方法
public class CheesyCounter {
private volatile int value;
//讀操作逗堵,沒有synchronized眷昆,提高性能
public int getValue() {
return value;
}
//寫操作,必須synchronized亚斋。因?yàn)閤++不是原子操作
public synchronized int increment() {
return value++;
}
}
synchronized
synchronized一般被稱為重量級鎖帅刊。但是jdk1.6之后對它進(jìn)行了許多優(yōu)化兄旬,減少了獲得鎖和釋放鎖的性能消耗录肯。
- java中synchronized加鎖的三種形式
- 對于普通同步方法栏饮,鎖的為當(dāng)前實(shí)例對象
- 對于靜態(tài)同步方法,鎖的是當(dāng)前類的Class對象
- 對于同步塊內(nèi)的方法境蔼,鎖的為括號內(nèi)配置的對象
public class ThreeLock {
//鎖當(dāng)前對象
synchronized public void print() {
System.out.println(System.currentTimeMillis());
}
//同步塊鎖當(dāng)前對象
public void lock1() {
synchronized (this) {
System.out.println(System.currentTimeMillis());
}
}
//同步快鎖當(dāng)前類的Class對象
public void lock2() {
synchronized (ThreeLock.class) {
System.out.println(System.currentTimeMillis());
}
}
// 靜態(tài)方法鎖當(dāng)前類的class對象
public synchronized static void lock3() {
}
}
-
synchronized的原理
被synchronized修飾的對象欧穴,每個對象都有一個Monitor對象泵殴,獲得鎖時,調(diào)用monitorenter笑诅,退出鎖時使用monitorexit。因?yàn)閟ynchronized是可重入鎖弦叶,我們可以理解為獲得鎖時妇多,monitor++,退出鎖時monitor--立莉,當(dāng)為0時七问,為無鎖狀態(tài)。
鎖的升級與對比
java1.6 為了減少獲得鎖和釋放鎖的性能消耗械巡,引入了偏向鎖和輕量級鎖,在jdk1.6中有勾,鎖一共有4種狀態(tài):無鎖狀態(tài)葛账、偏向鎖狀態(tài)、輕量級鎖狀態(tài)菲宴、重量級鎖狀態(tài)
- 偏向鎖
HotSpotjvm的作者經(jīng)過研究發(fā)現(xiàn)趋急,大多數(shù)情況下,鎖不僅不存在多線程競爭谣蠢,而且總是由同
一線程多次獲得,為了讓線程獲得鎖的代價(jià)更低而引入了偏向鎖眉踱。當(dāng)一個線程訪問同步塊并
獲取鎖時,會在對象頭和棧幀中的鎖記錄里存儲鎖偏向的線程ID册烈,以后該線程在進(jìn)入和退出
同步塊時不需要進(jìn)行CAS操作來加鎖和解鎖婿禽,只需簡單地測試一下對象頭的Mark Word里是否
存儲著指向當(dāng)前線程的偏向鎖。如果測試成功淀零,表示線程已經(jīng)獲得了鎖膛壹。如果測試失敗,則需
要再測試一下Mark Word中偏向鎖的標(biāo)識是否設(shè)置成1(表示當(dāng)前是偏向鎖):如果沒有設(shè)置哀卫,則
使用CAS競爭鎖撬槽;如果設(shè)置了,則嘗試使用CAS將對象頭的偏向鎖指向當(dāng)前線程共啃。 - 輕量級鎖
輕量級鎖原理非常簡單暂题,如果持有鎖的線程能在很短時間內(nèi)釋放鎖資源,那么那些等待競爭鎖的線程就不需要做內(nèi)核態(tài)和用戶態(tài)之間的切換進(jìn)入阻塞掛起狀態(tài)薪者,它們只需要等一等(自旋)言津,等持有鎖的線程釋放鎖后即可立即獲取鎖,這樣就避免用戶線程和內(nèi)核的切換的消耗悬槽。
線程在執(zhí)行同步塊之前,JVM會先在當(dāng)前線程的棧楨中創(chuàng)建用于存儲鎖記錄的空間蓬坡,并
將對象頭中的Mark Word復(fù)制到鎖記錄中,官方稱為Displaced Mark Word萨赁。然后線程嘗試使用
CAS將對象頭中的Mark Word替換為指向鎖記錄的指針兆龙。如果成功,當(dāng)前線程獲得鎖,如果失
敗臣缀,表示其他線程競爭鎖,當(dāng)前線程便嘗試使用自旋來獲取鎖计寇。 - 重量級鎖
使用監(jiān)視器鎖來實(shí)現(xiàn)脂倦,底層調(diào)用的是操作系統(tǒng)的互斥鎖(mutexLock),系統(tǒng)檢查到鎖是重量級鎖之后蝶押,會把等待想要獲得鎖的線程進(jìn)行阻塞火欧,被阻塞的線程不會消耗cup。但是阻塞或者喚醒一個線程時赶盔,都需要操作系統(tǒng)來幫忙榆浓,這就需要從用戶態(tài)轉(zhuǎn)換到內(nèi)核態(tài),而轉(zhuǎn)換狀態(tài)是需要消耗很多時間的烘浦,有可能比用戶執(zhí)行代碼的時間還要長萍鲸。