關(guān)于ReentrantLock、Condition和ReadWriteLock的學(xué)習(xí)筆記

ReentrantLock、Condition及ReadWriteLock

先看一下ReentrantLock和Condition

  • 以一個(gè)簡(jiǎn)單的ReentrantLock和Condition的例子為入口

    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class ConditionDemo {
    
        public static void main(String[] args) {
            final ReentrantLock lock = new ReentrantLock();
            final Condition condition = lock.newCondition();
    
            Thread thread1 = new Thread(new Runnable() {
                public void run() {
                    try {
                        lock.lock();
                        // Thread1首先獲得鎖锄开,然后調(diào)用condition.await()等待信號(hào)
                        System.out.println("Thread1 get lock, then wait for new signal");
                        condition.await();
                        // 當(dāng)Thread2中調(diào)用condition.signal()/signalAll()之后,Thread1被喚醒繼續(xù)執(zhí)行
                        System.out.println("Thread1 got new signal and continue...");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                        System.out.println("Thread1 unlock");
                    }
                }
            });
            thread1.start();
    
            Thread thread2 = new Thread(new Runnable() {
                public void run() {
                    lock.lock();
                    // Thread2獲得鎖
                    System.out.println("Thread2 get lock, then send a signal.");
                    // 調(diào)用signalAll()通知所有等待狀態(tài)的線程
                    condition.signalAll();// 此時(shí)Thread1被喚醒,并開(kāi)始爭(zhēng)奪鎖,但是由于thread2還沒(méi)有釋放鎖莹妒,因此無(wú)法繼續(xù)運(yùn)行,thread1繼續(xù)阻塞
                    
                    System.out.println("Thread2 sent a signal and continue");
                    try {
                        Thread.sleep(1000);// 等待1秒鐘后再釋放鎖
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    lock.unlock();
                    // Thread2釋放鎖之后Thread1重新獲得鎖繼續(xù)運(yùn)行
                    System.out.println("Thread2 unlock");
                }
            });
            thread2.start();
        }
    }
    
  • 執(zhí)行結(jié)果

    Thread1 get lock, then wait for new signal
    Thread2 get lock, then send a signal.
    Thread2 sent a signal and continue
    Thread2 unlock
    Thread1 got new signal and continue...
    Thread1 unlock
    
  • 從上面的結(jié)果可以看出旨怠,thread1執(zhí)行之后首先獲得了鎖,但是隨后執(zhí)行了condition.await()之后又釋放了鎖進(jìn)入等待狀態(tài)蜈块。然后thread2執(zhí)行之后獲得鎖,再發(fā)送signal給thread1,但此時(shí)還沒(méi)有釋放鎖课锌,所以thread1依舊阻塞直到一秒后thread2釋放鎖thread1才能繼續(xù)運(yùn)行述雾。

  • 因此Condition非常適合線程間的通信,下面以消費(fèi)者生產(chǎn)者的例子來(lái)說(shuō)明展示Condition的強(qiáng)大

    地主家的傻兒子和慢慢收租的地主

  • 銀行卡

    public class BankCard {
        private Long balance = 1000000L;
    
        public BankCard(Long balance) {
            this.balance = balance;
        }
    
        public BankCard() {
    
        }
    
        public Long getBalance() {
            return balance;
        }
    
        public void setBalance(Long balance) {
            this.balance = balance;
        }
    }
    
  • 傻兒子

    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    
    public class StupidSon implements Runnable {
    
        private BankCard bankCard;
        private int id;
        private Lock lock;
        private Condition condition;
    
        public StupidSon(BankCard bankCard, int id, Lock lock, Condition condition) {
            this.bankCard = bankCard;
            this.id = id;
            this.lock = lock;
            this.condition = condition;
        }
    
        public void run() {
            try {
                while (true) {
                    lock.lock();
                    // 傻兒子每次消費(fèi)1000匣掸,當(dāng)余額不足時(shí)等老父親賺錢存到銀行卡
                    while (bankCard.getBalance() - 1000 <= 0) {
                        System.out.println("StupidSon" + id + ", $" + bankCard.getBalance() + " no more money, wait for father's signal..");
                        condition.await();
                    }
                    bankCard.setBalance(bankCard.getBalance() - 1000);
                    System.out.println("StupidSon" + id + " cost $1000, remains:" + bankCard.getBalance());
                    lock.unlock();
                    // 一秒鐘消費(fèi)一次
                    Thread.sleep(1000L);
                }
            } catch (Exception e) {
                e.printStackTrace();
                lock.unlock();
            }
        }
    }
    
  • 老父親

    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    
    public class HardFather implements Runnable {
    
        private BankCard bankCard;
        private Lock lock;
        private Condition condition;
    
        public HardFather(BankCard bankCard, Lock lock, Condition condition) {
            this.bankCard = bankCard;
            this.lock = lock;
            this.condition = condition;
        }
    
        public void run() {
    
            while (true) {
                lock.lock();
                bankCard.setBalance(bankCard.getBalance() + 500);
                System.out.println("Father earn $500, remains:" + bankCard.getBalance());
                if (bankCard.getBalance() > 1000 && bankCard.getBalance() < 2000) {
                    // 當(dāng)金額超過(guò)1000之后告訴傻兒子們可以來(lái)拿錢了
                    condition.signalAll();
                    System.out.println("Money above 1000 again, sent signal.");
                }
                lock.unlock();
                try {
                    // 三秒存一次錢
                    Thread.sleep(3000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
    
        }
    }
    
  • 測(cè)試實(shí)例

    public class Main {
        public static void main(String[] args) {
              // 初始化金額為1萬(wàn)
            BankCard bankCard = new BankCard(10000L);
            Lock lock = new ReentrantLock();
            Condition condition = lock.newCondition();
              // 這里創(chuàng)建兩個(gè)兒子
            StupidSon son1 = new StupidSon(bankCard, 1, lock, condition);
            StupidSon son2 = new StupidSon(bankCard, 2, lock, condition);
              // 一個(gè)父親橄杨,公用一個(gè)lock和同一個(gè)condition
            HardFather father = new HardFather(bankCard, lock, condition);
              // 放入線程池執(zhí)行
            ExecutorService pool = Executors.newCachedThreadPool();
            pool.submit(father);
            pool.submit(son1);
            pool.submit(son2);
        }
    }
    
    
  • 執(zhí)行結(jié)果

    Father earn $500, remains:10500
    StupidSon1 cost $1000, remains:9500
    StupidSon2 cost $1000, remains:8500
    StupidSon1 cost $1000, remains:7500
    StupidSon2 cost $1000, remains:6500
    StupidSon1 cost $1000, remains:5500
    StupidSon2 cost $1000, remains:4500
    Father earn $500, remains:5000
    StupidSon1 cost $1000, remains:4000
    StupidSon2 cost $1000, remains:3000
    StupidSon1 cost $1000, remains:2000
    StupidSon2 cost $1000, remains:1000
    StupidSon1, $1000 no more money, wait for father's signal..
    StupidSon2, $1000 no more money, wait for father's signal..
    Father earn $500, remains:1500
    Money above 1000 again, sent signal.
    StupidSon1 cost $1000, remains:500
    StupidSon2, $500 no more money, wait for father's signal..
    StupidSon1, $500 no more money, wait for father's signal..
    Father earn $500, remains:1000
    Father earn $500, remains:1500
    Money above 1000 again, sent signal.
    StupidSon2 cost $1000, remains:500
    StupidSon1, $500 no more money, wait for father's signal..
    StupidSon2, $500 no more money, wait for father's signal..
    Father earn $500, remains:1000
    Father earn $500, remains:1500
    Money above 1000 again, sent signal.
    StupidSon1 cost $1000, remains:500
    StupidSon2, $500 no more money, wait for father's signal..
    
  • 從結(jié)果可以看出惯悠,當(dāng)金額少于1000之后兩個(gè)兒子都在等待父親的信號(hào),然后當(dāng)他們的父親存錢達(dá)到1500之后發(fā)送信號(hào)睁宰,這兩個(gè)兒子便開(kāi)始爭(zhēng)奪鎖然后消耗金額繼續(xù)等待刹前。這其實(shí)就是最基本的一個(gè)生產(chǎn)者消費(fèi)者問(wèn)題耍目,使用ReentrantLock和Conditon之后處理生產(chǎn)者消費(fèi)者問(wèn)題就變得非常簡(jiǎn)單沮榜。

下面來(lái)看一下讀寫(xiě)鎖ReadWriteLock

  • 銀行卡使用上例中的就不貼代碼了

  • 傻兒子如下

    
    import java.util.concurrent.locks.ReadWriteLock;
    
    public class StupidSon implements Runnable {
    
        private ReadWriteLock lock;
        private int id;
        private BankCard bankCard;
    
        public StupidSon(ReadWriteLock lock, BankCard bankCard, int id) {
            this.lock = lock;
            this.bankCard = bankCard;
            this.id = id;
        }
    
        public void run() {
            int cost = 1000;
            while (true) {
                lock.writeLock().lock();
                // 獲取寫(xiě)鎖,讀鎖和寫(xiě)鎖是互斥、寫(xiě)鎖和寫(xiě)鎖也是互斥的拒担,此時(shí)其他線程讀嘹屯、寫(xiě)鎖都將無(wú)法獲取只能等待
                if (bankCard.getBalance() - cost < 0) {
                    System.out.println("StupidSon" + id + " no more money");
                    lock.writeLock().unlock();
                    // 判斷金額不足之后釋放寫(xiě)鎖,跳出while循環(huán)結(jié)束線程
                    break;
                }
                bankCard.setBalance(bankCard.getBalance() - cost);
                System.out.println("StupidSon" + id + " cost $" + cost + ", remain:" + bankCard.getBalance());
                try {
                    // 等待500毫秒之后再釋放寫(xiě)鎖
                    System.out.print("StupidSon" + id + " wait for 500ms...");
                    Thread.sleep(500);
                    // 修改金額之后釋放寫(xiě)鎖从撼,此時(shí)其他線程可以繼續(xù)競(jìng)爭(zhēng)獲取寫(xiě)鎖和讀鎖
                    lock.writeLock().unlock();
                    System.out.println("StupidSon" + id + "release writeLock.");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("StupidSon" + id + " exit.");
        }
    }
    
  • 父母類 起到監(jiān)督金額的作用

    
    import java.util.concurrent.locks.ReadWriteLock;
    
    
    public class Parents implements Runnable {
    
        private ReadWriteLock lock;
        private BankCard bankCard;
        private int type;
    
        public Parents(ReadWriteLock lock, BankCard bankCard, int type) {
            this.lock = lock;
            this.bankCard = bankCard;
            this.type = type;
        }
    
        public void run() {
            String mof = type == 1 ? "Mother" : "Father";
            while (true) {
                lock.readLock().lock();
                // 獲取讀鎖州弟, 讀鎖和讀鎖是不互斥的 也就是說(shuō)多個(gè)線程可以同時(shí)獲取讀鎖钧栖,但是讀寫(xiě)鎖是互斥的,此時(shí)寫(xiě)鎖無(wú)法被獲得只能等待
                if (bankCard.getBalance() <= 0) {
                    System.out.println(mof + " saw no more money. stop check!");
                    lock.readLock().unlock();// 這里一定要釋放讀鎖婆翔,否則會(huì)導(dǎo)致死鎖
                    break;
                }
                System.out.println(mof + " check balance:" + bankCard.getBalance());
                try {
                    // 等待500毫秒之后再釋放讀鎖
                    System.out.print(mof + " wait for 500ms...");
                    Thread.sleep(500);
                    System.out.println(mof + " release readLock.");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 讀取結(jié)束后釋放讀鎖拯杠,寫(xiě)鎖可以被其他線程競(jìng)爭(zhēng)獲取到
                lock.readLock().unlock();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(mof+" exit");
        }
    }
    
  • 測(cè)試

    
    public class Main {
        public static void main(String[] args) {
            ReadWriteLock lock = new ReentrantReadWriteLock();
            BankCard bankCard = new BankCard(10000L);
              // 初始化四個(gè)兒子和兩老
            Runnable son = new StupidSon(lock, bankCard, 1);
            Runnable son2 = new StupidSon(lock, bankCard, 2);
            Runnable son3 = new StupidSon(lock, bankCard, 3);
            Runnable son4 = new StupidSon(lock, bankCard, 4);
            Runnable mother = new Parents(lock, bankCard, 1);
            Runnable father = new Parents(lock, bankCard, 2);
            ExecutorService threadPoolExecutor = Executors.newCachedThreadPool();
    
            threadPoolExecutor.submit(son3);
            threadPoolExecutor.submit(son4);
            threadPoolExecutor.submit(son2);
            threadPoolExecutor.submit(mother);
            threadPoolExecutor.submit(father);
            threadPoolExecutor.submit(son);
        }
    }
    
  • 運(yùn)行結(jié)果

    StupidSon4 cost $1000, remain:9000
    StupidSon4 wait for 500ms...StupidSon4release writeLock.
    StupidSon2 cost $1000, remain:8000
    StupidSon2 wait for 500ms...StupidSon2release writeLock.
    StupidSon3 cost $1000, remain:7000
    StupidSon3 wait for 500ms...StupidSon3release writeLock.
    Mother check balance:7000
    // 從這里可以看出讀鎖不互斥,因?yàn)槟赣H未釋放讀鎖的時(shí)候父親依舊可以獲取讀鎖
    Mother wait for 500ms...Father check balance:7000
    Father wait for 500ms...Father release readLock.
    Mother release readLock.
    StupidSon1 cost $1000, remain:6000
    StupidSon1 wait for 500ms...StupidSon1release writeLock.
    StupidSon4 cost $1000, remain:5000
    StupidSon4 wait for 500ms...StupidSon4release writeLock.
    StupidSon2 cost $1000, remain:4000
    StupidSon2 wait for 500ms...StupidSon2release writeLock.
    StupidSon3 cost $1000, remain:3000
    StupidSon3 wait for 500ms...StupidSon3release writeLock.
    StupidSon4 cost $1000, remain:2000
    StupidSon4 wait for 500ms...StupidSon4release writeLock.
    StupidSon2 cost $1000, remain:1000
    StupidSon2 wait for 500ms...StupidSon2release writeLock.
    Mother check balance:1000
    Mother wait for 500ms...Father check balance:1000
    Father wait for 500ms...Father release readLock.
    Mother release readLock.
    StupidSon1 cost $1000, remain:0
    StupidSon1 wait for 500ms...StupidSon1release writeLock.
    StupidSon3 no more money
    StupidSon3 exit.
    StupidSon4 no more money
    StupidSon4 exit.
    StupidSon2 no more money
    StupidSon2 exit.
    Father saw no more money. stop check!
    Father exit
    Mother saw no more money. stop check!
    Mother exit
    StupidSon1 no more money
    StupidSon1 exit.
    
  • 由上面的結(jié)果可以很直觀的了解到讀寫(xiě)鎖互斥啃奴、寫(xiě)鎖也寫(xiě)鎖之間互斥潭陪、讀鎖和讀鎖之間不互斥。

  • 讀寫(xiě)鎖的使用時(shí)一定要注意的是要及時(shí)釋放鎖最蕾,否則容易導(dǎo)致死鎖的發(fā)生依溯,最保險(xiǎn)的方式是將unlock代碼放在finally代碼塊中去

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市瘟则,隨后出現(xiàn)的幾起案子黎炉,更是在濱河造成了極大的恐慌,老刑警劉巖醋拧,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慷嗜,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡丹壕,警方通過(guò)查閱死者的電腦和手機(jī)洪添,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)雀费,“玉大人干奢,你說(shuō)我怎么就攤上這事≌蛋溃” “怎么了忿峻?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)辕羽。 經(jīng)常有香客問(wèn)我逛尚,道長(zhǎng),這世上最難降的妖魔是什么刁愿? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任绰寞,我火速辦了婚禮,結(jié)果婚禮上铣口,老公的妹妹穿的比我還像新娘滤钱。我一直安慰自己,他們只是感情好脑题,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布件缸。 她就那樣靜靜地躺著,像睡著了一般叔遂。 火紅的嫁衣襯著肌膚如雪他炊。 梳的紋絲不亂的頭發(fā)上争剿,一...
    開(kāi)封第一講書(shū)人閱讀 51,754評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音痊末,去河邊找鬼蚕苇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛凿叠,可吹牛的內(nèi)容都是我干的捆蜀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼幔嫂,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了誊薄?” 一聲冷哼從身側(cè)響起履恩,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎呢蔫,沒(méi)想到半個(gè)月后切心,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡片吊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年绽昏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片俏脊。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡全谤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出爷贫,到底是詐尸還是另有隱情认然,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布漫萄,位于F島的核電站卷员,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏腾务。R本人自食惡果不足惜毕骡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望岩瘦。 院中可真熱鬧未巫,春花似錦、人聲如沸启昧。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)箫津。三九已至狭姨,卻和暖如春宰啦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背饼拍。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工赡模, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人师抄。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓漓柑,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親叨吮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子辆布,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

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