悲觀鎖
悲觀鎖是指數(shù)據(jù)被外界修改保持保守狀態(tài),認(rèn)為數(shù)據(jù)很容易會(huì)被其他線程修改,所以在數(shù)據(jù)的處理錢(qián)先對(duì)數(shù)據(jù)進(jìn)行加鎖處理,并在整個(gè)數(shù)據(jù)的處理過(guò)程中,使得數(shù)據(jù)處理鎖定的狀態(tài)
樂(lè)觀鎖
樂(lè)觀鎖是相對(duì)于悲觀鎖,他認(rèn)為數(shù)據(jù)在一般的情況下不會(huì)造成沖突,所以在訪問(wèn)記錄前不會(huì)加排他鎖,而是在數(shù)據(jù)提交更新的時(shí)候才會(huì)正式對(duì)數(shù)據(jù)沖突與否進(jìn)行檢測(cè)。
公平鎖
根據(jù)線程獲取鎖的搶占機(jī)制,區(qū)分公平鎖和非公平鎖,公平鎖表示線程獲取鎖的順序按照線程請(qǐng)求鎖的時(shí)間早晚來(lái)決定,就是最早請(qǐng)求鎖的線程將最早獲取到鎖
非公平鎖
線程獲取鎖的順序并不一定按照線程的先后順序,比如A B C 3個(gè)線程,A獲取鎖了,那么B C 線程自然會(huì)被掛起,當(dāng)A釋放鎖后,如果采取公平鎖,那么C會(huì)被改期,B獲取鎖,而非公平鎖的話,可能C先獲取鎖
獨(dú)占鎖 非獨(dú)占鎖
根據(jù)鎖只能被單個(gè)線程持有還是多個(gè)線程持有分為獨(dú)占鎖和非獨(dú)占鎖
ReentrantLock就是以獨(dú)占方式實(shí)現(xiàn)的。共享鎖則可以同時(shí)由多個(gè)線程持有,例如ReadWriteLock讀寫(xiě)鎖,它允許一個(gè)資源可以被多線程同時(shí)進(jìn)行讀操作。
ReadWriteLock:
一次只有一個(gè)線程可以占有寫(xiě)模式的讀寫(xiě)鎖,但是多個(gè)線程可以同時(shí)占有讀模式的讀寫(xiě)鎖。
只有一個(gè)線程可以占有寫(xiě)狀態(tài)的鎖,但可以有多個(gè)線程同時(shí)占有讀狀態(tài)鎖,這也是它可以實(shí)現(xiàn)高并發(fā)的原因。當(dāng)其處于寫(xiě)狀態(tài)鎖下,任何想要嘗試獲得鎖的線程都會(huì)被阻塞,直到寫(xiě)狀態(tài)鎖被釋放;如果是處于讀狀態(tài)鎖下,允許其它線程獲得它的讀狀態(tài)鎖,但是不允許獲得它的寫(xiě)狀態(tài)鎖,直到所有線程的讀狀態(tài)鎖被釋放;為了避免想要嘗試寫(xiě)操作的線程一直得不到寫(xiě)狀態(tài)鎖,當(dāng)讀寫(xiě)鎖感知到有線程想要獲得寫(xiě)狀態(tài)鎖時(shí),便會(huì)阻塞其后所有想要獲得讀狀態(tài)鎖的線程。所以讀寫(xiě)鎖非常適合資源的讀操作遠(yuǎn)多于寫(xiě)操作的情況。
可重入鎖
當(dāng)一個(gè)線程要獲取一個(gè)被其他線程持有的獨(dú)占鎖時(shí),該線程會(huì)被阻塞,那么當(dāng)一個(gè)線程再次獲取它自己已經(jīng)獲鎖,如果不被阻塞那么就說(shuō)他是可重入鎖
synchronized內(nèi)部鎖是可重入鎖。可重入鎖的原理是在鎖內(nèi)部維護(hù)一個(gè)線程標(biāo)示,用來(lái)標(biāo)示該鎖目前被哪個(gè)線程占用,然后關(guān)聯(lián)一個(gè)計(jì)數(shù)器。一開(kāi)始
器值為0,說(shuō)明該鎖沒(méi)有被任何線程占用。當(dāng)一個(gè)線程獲取了該鎖時(shí),計(jì)數(shù)器的值會(huì)變成1,這時(shí)其他線程再來(lái)獲取該鎖時(shí)會(huì)發(fā)現(xiàn)鎖的所有者不是自己而被阻塞掛起。
但是當(dāng)獲取了該鎖的線程再次獲取鎖時(shí)發(fā)現(xiàn)鎖擁有者是自己,就會(huì)把計(jì)數(shù)器值加+1,當(dāng)釋放鎖后計(jì)數(shù)器值-1。當(dāng)計(jì)數(shù)器值為0時(shí),鎖里面的線程標(biāo)示被重置為null,這時(shí)候被阻塞的線程會(huì)被喚醒來(lái)競(jìng)爭(zhēng)獲取該鎖。
/**
* 可重入鎖,如果不是可重入鎖的話,首先明確鎖的是這個(gè)類(lèi),獲取的是這個(gè)類(lèi)的鎖,
* 然后 調(diào)用setA 的時(shí)候獲取了這個(gè)類(lèi)的鎖了,如果不是可重入鎖的話,那么SETB()
* 就無(wú)法獲取鎖,導(dǎo)致這個(gè)setB 這個(gè)方法沒(méi)法執(zhí)行
*/
public static class TestLock{
private int a;
private int b;
public static synchronized void setA() {
System.out.println("locak a");
setB();
}
public static synchronized void setB() {
System.out.println("lock b");
}
}
public static void main (String[] args){
TestLock.setA();
}

自旋鎖
Java中的線程是與操作系統(tǒng)中的線程一一對(duì)應(yīng)的,所以當(dāng)一個(gè)線程在獲取鎖(比如獨(dú)占鎖)失敗后,會(huì)被切換到內(nèi)核狀態(tài)而被掛起。當(dāng)該線程獲取到鎖時(shí)又需要將其切換到內(nèi)核狀態(tài)而喚醒該線程。而從用戶狀態(tài)切換到內(nèi)核狀態(tài)的開(kāi)銷(xiāo)是比較大的,在一定程度上會(huì)影響并發(fā)性能。自旋鎖則是,當(dāng)前線程在獲取鎖時(shí),如果發(fā)現(xiàn)鎖已經(jīng)被其他線程占有,它不馬上阻塞自己,在不放棄CPU使用權(quán)的情況下,多
次嘗試獲?。J(rèn)次數(shù)是10,可以使用-XX:PreBlockSpinsh參數(shù)設(shè)置該值),很有可能在后面幾次嘗試中其他線程已經(jīng)釋放了鎖。如果嘗試指定的次數(shù)后仍沒(méi)有獲取到鎖則當(dāng)前線程才會(huì)被阻塞掛起。由此看來(lái)自旋鎖是使用CPU時(shí)間換取線程阻塞與調(diào)度的開(kāi)銷(xiāo),但是很有可能這些CPU時(shí)間白白浪費(fèi)了。
CAS算法
CAS是英文單詞Compare and Swap(比較并交換),是一種有名的無(wú)鎖算法。無(wú)鎖編程,即不使用鎖的情況下實(shí)現(xiàn)多線程之間的變量同步,也就是在沒(méi)有線程被阻塞的情況下實(shí)現(xiàn)變量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。CAS算法涉及到三個(gè)操作數(shù)
1.需要讀寫(xiě)的內(nèi)存值 V
2.進(jìn)行比較的值 A
3.擬寫(xiě)入的新值 B
更新一個(gè)變量的時(shí)候,只有當(dāng)變量的預(yù)期值A(chǔ)和內(nèi)存地址V當(dāng)中的實(shí)際值相同時(shí),才會(huì)將內(nèi)存地址V對(duì)應(yīng)的值修改為B,否則不會(huì)執(zhí)行任何操作。一般情況下是一個(gè)自旋操作,即不斷的重試。