Java并發(fā)編程-讀寫鎖(ReentrantReadWriteLock)

章節(jié)目錄

  • ReentrantReadWriteLock 特性
  • 讀寫鎖接口示例
  • 讀寫鎖的實(shí)現(xiàn)分析
    • 讀寫狀態(tài)設(shè)計(jì)
    • 寫鎖的釋放與獲取
    • 讀鎖的釋放與獲取
    • 鎖降級(jí)

1. ReentrantReadWriteLock 特性

1.1 讀寫鎖定義

讀寫鎖維護(hù)了一對(duì)鎖珍德,一個(gè)讀鎖熄云,一個(gè)寫鎖,通過分離讀鎖寫鎖,使得并發(fā)性相比一般的排他鎖有了很大提升。

1.2 讀寫鎖使用場(chǎng)景

1.讀寫鎖比較適用于讀多寫少的應(yīng)用場(chǎng)景。
2.讀寫鎖在統(tǒng)一時(shí)刻可以允許多個(gè)讀線程訪問,但是在寫線程訪問時(shí),所有的讀線程拦止、其他寫線程均被阻塞。

1.3 讀寫鎖的優(yōu)點(diǎn)

1.保證寫操作對(duì)讀操作的可見性
2.在讀多寫少的情況下的并發(fā)性的提升
3.讀寫鎖簡化可讀寫交互場(chǎng)景的編程方式

2.讀寫鎖接口示例

如下為使用讀寫鎖操作緩存的示例

package org.seckill.lock;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteCache {
    //充當(dāng)cache
    static Map<String, Object> map = new HashMap<String, Object>();
    //實(shí)例化讀寫鎖對(duì)象
    static ReentrantReadWriteLock reentrantReadWriteLock =
            new ReentrantReadWriteLock();
    //實(shí)例化讀鎖
    static Lock r = reentrantReadWriteLock.readLock();
    //實(shí)例化寫鎖
    static Lock w = reentrantReadWriteLock.writeLock();

    //獲取緩存中值
    public static final Object get(String key) {
        r.lock();
        try {
            return map.get(key);
        } finally {
            r.unlock();
        }
    }

    //寫緩存中值糜颠,并返回對(duì)應(yīng)value
    public static final Object set(String key, Object obj) {
        w.lock();
        try {
            return map.put(key, obj);
        } finally {
            w.unlock();
        }
    }

    //清空所有內(nèi)容
    public static final void clear() {
        w.lock();
        try {
            map.clear();
        } finally {
            w.unlock();
        }
    }
}

如上所示:

1.Cache組合一個(gè)非線程安全的HashMap做為緩存實(shí)現(xiàn)汹族,同時(shí)使用讀寫鎖的
讀鎖和寫鎖來保證Cache是線程安全的。
2.在讀操作get(String key)方法中括蝠,需要使用讀鎖鞠抑,這使得并發(fā)訪問該方法時(shí)不
會(huì)被阻塞。
3.寫鎖put(String key,Object object)方法和clear()方法忌警,在更新HashMap時(shí)必須
提前獲取寫鎖搁拙,當(dāng)獲取寫鎖后,其他線程對(duì)于讀鎖和寫鎖的獲取都被阻塞法绵,只
有寫鎖釋放之后箕速,其他的讀寫操作才能繼續(xù)操作,也就是說寫鎖其實(shí)是排他
鎖朋譬、互斥鎖盐茎。
4.最終,讀鎖提升了讀操作的并發(fā)性徙赢,也保證了每次寫操作對(duì)所有后續(xù)讀操作
的可見性字柠,同時(shí)簡化了編程方式探越,對(duì)應(yīng)1.3
 

3.讀寫鎖的實(shí)現(xiàn)分析

3.1 讀寫狀態(tài)設(shè)計(jì)

1.讀寫鎖同樣依賴自定義同步器實(shí)現(xiàn)同步功能
2.ReentrantLock 中同步狀態(tài)表示鎖被一個(gè)線程重復(fù)獲取的次數(shù)。
3.讀寫鎖自定義同步器需要在同步狀態(tài)上維護(hù)多個(gè)讀線程和一個(gè)寫線程的狀態(tài)窑业。
4.讀寫鎖同步器采用在一個(gè)4字節(jié)的整形變量上使用 按位切割 的方式來維護(hù)讀
寫線程的同步狀態(tài)钦幔。高16位用來表示讀,低16位用來表示寫常柄。
5.寫狀態(tài)增加1鲤氢,表示當(dāng)前線程獲取寫鎖,則 Status = S(當(dāng)前同步狀態(tài))+1,當(dāng)讀
狀態(tài)加1時(shí)卷玉,Status = S+(1<<16)

3.2 寫鎖的獲取與釋放
如下源碼所示:

 protected final boolean tryAcquire(int acquires) {
            /*
             * Walkthrough:
             * 1. If read count nonzero or write count nonzero
             *    and owner is a different thread, fail.
             * 2. If count would saturate, fail. (This can only
             *    happen if count is already nonzero.)
             * 3. Otherwise, this thread is eligible for lock if
             *    it is either a reentrant acquire or
             *    queue policy allows it. If so, update state
             *    and set owner.
             */
            Thread current = Thread.currentThread();
            int c = getState();
            //獲取獨(dú)占鎖(寫鎖)的被獲取的數(shù)量
            int w = exclusiveCount(c);
            if (c != 0) {
                // (Note: if c != 0 and w == 0 then shared count != 0)
                //1.如果同步狀態(tài)不為0蚂子,且寫狀態(tài)為0,則表示當(dāng)前同步狀態(tài)被讀鎖獲取
                //2.或者當(dāng)前擁有寫鎖的線程不是當(dāng)前線程
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;
                if (w + exclusiveCount(acquires) > MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                // Reentrant acquire
                setState(c + acquires);
                return true;
            }
            if (writerShouldBlock() ||
                !compareAndSetState(c, c + acquires))
                return false;
            setExclusiveOwnerThread(current);
            return true;
        }

3.3 讀鎖的釋放與獲取

protected final int tryAcquireShared(int unused) {
    for(;;) {
        int c = getState();
        int nextc = c + (1<<16);
        if(nextc < c) {
           throw new Error("Maxumum lock count exceeded");
        }
        if(exclusiveCount(c)!=0 && owner != Thread.currentThread())
           return -1;
        if(compareAndSetState(c,nextc))
           return 1;
    }
}

如果其他線程獲取了寫鎖,則當(dāng)前線程獲取讀鎖失敗惧互,進(jìn)入等待狀態(tài)。
如果當(dāng)前線程獲取了寫鎖或者寫鎖未被獲取稻据,則當(dāng)前線程安全,依靠CAS保證增加讀狀態(tài)算柳,成功獲取鎖。

3.4 鎖降級(jí)

鎖降級(jí)是指當(dāng)前把持住寫鎖猪杭,再獲取到讀鎖,隨后釋放(先前擁有的)寫鎖的過程。

鎖降級(jí)過程中的讀鎖的獲取是否有必要,答案是必要的。主要是為了保證數(shù)據(jù)的可見性掂器,如果當(dāng)前線程不獲取讀鎖而直接釋放寫鎖,假設(shè)此刻另一個(gè)線程獲取的寫鎖乃摹,并修改了數(shù)據(jù),那么當(dāng)前線程就步伐感知到線程T的數(shù)據(jù)更新,如果當(dāng)前線程遵循鎖降級(jí)的步驟,那么線程T將會(huì)被阻塞,直到當(dāng)前線程使數(shù)據(jù)并釋放讀鎖之后抵皱,線程T才能獲取寫鎖進(jìn)行數(shù)據(jù)更新善榛。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末辩蛋,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子移盆,更是在濱河造成了極大的恐慌悼院,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咒循,死亡現(xiàn)場(chǎng)離奇詭異据途,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)叙甸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門颖医,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人裆蒸,你說我怎么就攤上這事熔萧。” “怎么了僚祷?”我有些...
    開封第一講書人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵佛致,是天一觀的道長。 經(jīng)常有香客問我辙谜,道長俺榆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任装哆,我火速辦了婚禮肋演,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘烂琴。我一直安慰自己,他們只是感情好蜕乡,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開白布奸绷。 她就那樣靜靜地躺著,像睡著了一般层玲。 火紅的嫁衣襯著肌膚如雪号醉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,829評(píng)論 1 290
  • 那天辛块,我揣著相機(jī)與錄音畔派,去河邊找鬼。 笑死润绵,一個(gè)胖子當(dāng)著我的面吹牛线椰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播尘盼,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼憨愉,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼烦绳!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起配紫,我...
    開封第一講書人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤径密,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后躺孝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體享扔,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年植袍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了惧眠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡奋单,死狀恐怖锉试,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情览濒,我是刑警寧澤呆盖,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站贷笛,受9級(jí)特大地震影響应又,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜乏苦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一株扛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧汇荐,春花似錦洞就、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至革娄,卻和暖如春倾贰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拦惋。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來泰國打工匆浙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人厕妖。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓首尼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子饰恕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容