Java鎖概念
在講這個(gè)類之前先簡單說一下Java并發(fā)兩個(gè)概念,共享鎖军掂,排他鎖(獨(dú)占鎖)屁魏。共享鎖就可以同時(shí)被多個(gè)線程占有的鎖,即允許多個(gè)線程同時(shí)獲取鎖诊杆,同時(shí)訪問資源歼捐,常見類:CountDownLatch。排他鎖就是在任何時(shí)刻只允許一個(gè)線程能獲取鎖晨汹,其他的線程都在等待獲取豹储,必須等到這個(gè)鎖持有者釋放了,才能獲取到鎖淘这,常見類:ReentrantLock剥扣。
ReentrantReadWriteLock介紹
ReentrantReadWriteLock 是Java的讀寫鎖巩剖,擁有共享鎖和排他鎖的功能,但并不是完全的共享鎖和排他鎖钠怯。通過分離讀寫鎖佳魔,讓多線程可以并發(fā)訪問讀鎖,在訪問寫鎖的時(shí)候晦炊,讀寫互斥鞠鲜,起到保護(hù)數(shù)據(jù)的目的。主要應(yīng)用場景是:當(dāng)很多線程從每個(gè)數(shù)據(jù)結(jié)構(gòu)讀取數(shù)據(jù)断国,而很少有線程對(duì)其進(jìn)行修改時(shí)镊尺,在這種情況下,允許讀取線程共享訪問你是合適的并思,寫入器線程依然是互斥庐氮。
ReentrantReadWriteLock 主要特性
公平性
支持公平鎖和非公平鎖,默認(rèn)是非公平鎖宋彼,可以根據(jù)構(gòu)造方式設(shè)置公平鎖弄砍。
ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(true);//默認(rèn)是false 非公平鎖
由于讀線程之間沒有鎖競爭,所以讀操作输涕,沒有公平性和非公平性音婶。寫操作時(shí)可能立即獲取到鎖,所以會(huì)推遲一個(gè)或者多個(gè)讀操作或者寫操作莱坎,非公平鎖的吞吐量要高于公平鎖衣式。
重入性
讀寫鎖允許讀線程和寫線程按照請(qǐng)求鎖的順序重新獲取讀取鎖或者寫入鎖,但是只有寫線程釋放了鎖檐什,讀線程才能獲取重入鎖碴卧。寫線程獲取寫入鎖以后可以再次獲取讀鎖,但是讀線程獲取讀鎖后不能獲取寫入鎖乃正。讀寫鎖最多支持65535個(gè)遞歸寫入鎖和65535個(gè)遞歸讀取鎖住册。
鎖降級(jí)
寫線程獲取鎖后,可以獲取讀線程瓮具,然后再釋放寫鎖荧飞,這樣就從寫鎖變成讀鎖,實(shí)現(xiàn)了鎖降級(jí)名党。
鎖升級(jí)
讀線程獲取鎖后叹阔,并不知直接獲取寫鎖,獲取一個(gè)寫入鎖需要釋放所有讀取鎖传睹。從讀取鎖升級(jí)到寫入鎖耳幢,這個(gè)過程被稱作鎖升級(jí),這個(gè)過程必須讀取鎖釋放了鎖蒋歌,在重新獲取到寫入鎖帅掘,才能稱作鎖升級(jí)委煤。
鎖獲取中斷
讀取鎖和寫入鎖都支持在獲取鎖期間被中斷。
概括起來其實(shí)就是讀寫鎖的機(jī)制
讀讀不互斥修档,即使當(dāng)前有100個(gè)線程同時(shí)讀取資源碧绞,沒有線程寫入,這個(gè)100個(gè)線程是可以并發(fā)訪問吱窝。
讀寫互斥讥邻,比如說當(dāng)前有線程在寫,當(dāng)線程想獲取讀入鎖的時(shí)候院峡,會(huì)被堵塞兴使,反過來,有線程已經(jīng)獲取讀入鎖的時(shí)候照激,寫的線程也會(huì)堵塞发魄,就看誰先拿到鎖。
寫寫互斥比如有兩個(gè)線程A俩垃,B励幼,A線程先拿到鎖,B線程就會(huì)堵塞到A釋放鎖為止口柳。
使用ReentrantReadWriteLock讀取緩存例子
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Map<String,Object> map = new HashMap<>(); //假設(shè)這存著數(shù)據(jù)緩存
public Object readwrite(String id) {
Object val = null;
rwLock.readLock().lock(); //首先開啟讀取鎖苹粟,從緩存中去讀取
try {
val = map.get(id);
if(val == null) {//假如緩存中沒有這個(gè)值,可以從數(shù)據(jù)庫讀取這個(gè)值跃闹,然后寫入到緩存中
rwLock.readLock().unlock();//這里用到的鎖升級(jí)嵌削,先釋放,在獲取
rwLock.writeLock().lock();
try {
//這里是從數(shù)據(jù)庫獲取數(shù)據(jù)
val = "aaa";
}finally {
rwLock.writeLock().unlock();//釋放寫入鎖望艺,讓緩存可以并發(fā)訪問
}
rwLock.readLock().lock();
}
}finally {
rwLock.readLock().unlock();//兩次讀取鎖苛秕,保證釋放鎖,一定都在finally中可以釋放
}
return val;
}