JAVA線程協(xié)作:wait() notify()notifyAll()

一、wait、notify谆棺、notifyAll綜述

wait/notify/notifyAll是JAVA提供通過對(duì)鎖的監(jiān)視的方式進(jìn)行線程間的協(xié)作。正因?yàn)橥ㄟ^對(duì)鎖的監(jiān)視鹃骂,因此wait()/notify()/notifyAll()方法的調(diào)用必須先獲得鎖,再調(diào)用鎖對(duì)象的wait()/notify()/notifyAll()方法。因此一般情況下配合synchronized進(jìn)行使用罢绽。

二畏线、交互過程

鎖的監(jiān)視器模式

在并發(fā)編程中,我們?cè)O(shè)計(jì)的對(duì)象有可能會(huì)被多線程進(jìn)行調(diào)用良价,而這種調(diào)用通常會(huì)改變對(duì)象的狀態(tài)寝殴,那么在并發(fā)編程的時(shí)候通常為了保證這種對(duì)象狀態(tài)改變的原子性,一般情況下會(huì)通過鎖的進(jìn)行處理明垢。那問題來了蚣常,如果多個(gè)線程在等待獲取鎖的情況下,JAVA是怎樣處理的呢袖外?JAVA對(duì)于鎖的釋放和協(xié)作通知是通過監(jiān)視器模式來進(jìn)行處理史隆。如下圖為一個(gè)鎖的釋放通知魂务,線程A獲取到鎖后曼验,線程B、C粘姜、D分別去取獲取鎖鬓照,發(fā)現(xiàn)鎖已經(jīng)被線程A獲取。線程B孤紧、C豺裆、D進(jìn)入阻塞狀態(tài),等待監(jiān)視器的通知。當(dāng)線程A釋放鎖后臭猜,鎖監(jiān)視器通知請(qǐng)求獲取鎖的隊(duì)列中的線程躺酒。


image.png

wait、notify蔑歌、notifyAll都是屬于Object的方法羹应,而在wait、notify次屠、notifyAll的設(shè)計(jì)上只有一個(gè)Condition隊(duì)列园匹,而在重入鎖ReentrantLock中則有多個(gè)Condition隊(duì)列。多個(gè)Condition隊(duì)列可以隔離每個(gè)Condition的等待和喚醒劫灶。ReentrantLock屬于編程式的鎖機(jī)制裸违,而wait、notify本昏、notifyAll通過synchronized進(jìn)行使用供汛,而synchronized屬于關(guān)鍵字,因此由JAVA內(nèi)部實(shí)現(xiàn)涌穆。

交互時(shí)序圖
交互過程.png

使用wait\notify\notify的目的是通過線程協(xié)作使并發(fā)線程串行化執(zhí)行紊馏。

  • Thread(wait)線程通過鎖對(duì)象獲取到鎖進(jìn)入到同步方法中執(zhí)行。然后通過鎖對(duì)象的wait方法蒲犬,通知鎖監(jiān)視器朱监,當(dāng)前線程掛起并且釋放鎖。

  • Thread(notify\noityall)線程獲取到鎖后執(zhí)行同步方法原叮,然后調(diào)用鎖對(duì)象的notify\notifyall方法赫编,告訴鎖監(jiān)視器,然后釋放鎖奋隶。鎖監(jiān)視器通知Thread(wait)線程擂送。

  • Thread(wait)線程收到鎖監(jiān)視器的通知后,恢復(fù)線程并且再次獲得鎖唯欣,然后執(zhí)行代碼嘹吨。

三、wait境氢、notify蟀拷、notifyAll標(biāo)準(zhǔn)范式

  • 等待方
    1.獲取鎖
    2.循環(huán)判斷是否符合條件,若不符合等待萍聊,若符合執(zhí)行業(yè)務(wù)邏輯
  • 通知方
    1.獲取鎖
    2.執(zhí)行業(yè)務(wù)邏輯
    3.通知所有等待方问芬。
    public static class BookARoomNotification {

        public boolean isNotification = false;

        public void waitNotification() {
            synchronized (this) {
                while (!isNotification) {
                    try {
                        wait();
                        System.out.println("book a room");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        public void notification() {
            synchronized (this) {
                this.isNotification = true;
                System.out.println("Booking Time");
                notifyAll();
            }
        }
    }

四、notify寿桨、notifyAll的區(qū)別

notify.png
notifyall.png

通過上面圖的分析我們應(yīng)該使用notifyall而非notify此衅。

五、實(shí)現(xiàn)等待超時(shí)

1、wait(long timeout)

JAVA為我們提供了wait的方法還給我們提供了等待超時(shí)方法wait(long timeout)挡鞍。而我們可以通過wait(long timeout)來實(shí)現(xiàn)等待超時(shí)骑歹。我們通過標(biāo)準(zhǔn)范式的修改實(shí)現(xiàn)等待超時(shí)。

2墨微、實(shí)現(xiàn)超時(shí)等待的標(biāo)準(zhǔn)范式
  • 等待方:
    (1) 獲取鎖
    (2)根據(jù)傳入的超時(shí)時(shí)間確定結(jié)束時(shí)間
long overTime = System.currentTimeMillis() +timeMillis;

(3)進(jìn)行等待超時(shí)并且循環(huán)判斷條件和當(dāng)前時(shí)間是否已經(jīng)超時(shí)陵刹。

long remain = timeMillis;
while(pool.isEmpty()&&remain>0) {
    pool.wait(timeMillis);
    remain = overTime - System.currentTimeMillis();
}

(4)超時(shí)后,繼續(xù)判斷條件是否符合欢嘿,符合執(zhí)行業(yè)務(wù)邏輯衰琐。

Connection connection= null;
if(!pool.isEmpty()) {
        connection = pool.removeFirst();
}
  • 通知方
    (1)獲取鎖
    (2)執(zhí)行業(yè)務(wù)邏輯
    (3)通知所有等待方。
3炼蹦、獲取連接池超時(shí)等待

DBPool.java

public class DBPool {

    private LinkedList<Connection> pool = new LinkedList<>();

    public DBPool(int initalSize) {
        super();
        for (int i = 0; i < initalSize; i++) {
            pool.addLast(MysqlConnection.fetchConnection());
        }
    }

    public int getPoolCount() {
        return pool.size();
    }
    
    public Connection fetchConnection(long timeMillis) throws InterruptedException {
        synchronized (pool) {
            if (timeMillis <= 0) {
                while (pool.isEmpty()) {
                    pool.wait();
                }
                return pool.removeFirst();
            } else {
                long overTime = System.currentTimeMillis() +timeMillis;
                long remain = timeMillis;
                while(pool.isEmpty()&&remain>0) {
                    pool.wait(timeMillis);
                    remain = overTime - System.currentTimeMillis();
                }
                Connection connection= null;
                if(!pool.isEmpty()) {
                    connection = pool.removeFirst();
                }
                return connection;
            }
        }
    }
    
    public void closeConnection(Connection conn) {
        if(conn!=null) {
            synchronized (pool) {
                pool.addLast(conn);
                pool.notifyAll();
            }
        }
    }

}

MysqlConnection.java

其他方法默認(rèn)實(shí)現(xiàn)即可
public class MysqlConnection implements Connection{
    /*拿一個(gè)數(shù)據(jù)庫連接*/
    public static final Connection fetchConnection(){
        return new MysqlConnection();
    }
    @Override
    public void commit() throws SQLException {
        try {
            Thread.sleep(70);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
        @Override
    public Statement createStatement() throws SQLException {
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

DBpoolTest.java

public class DBpoolTest {

    static DBPool pool = new DBPool(10);
    static CountDownLatch countDownLatch;
    static int threadCount = 50;
    static int getConnCount = 20;

    static class GetConnectionThread implements Runnable {
        int count;
        AtomicLong got;
        AtomicLong notGot;

        public GetConnectionThread(int count, AtomicLong got, AtomicLong notGot) {
            super();
            this.count = count;
            this.got = got;
            this.notGot = notGot;
        }
        @Override
        public void run() {
            while (count > 0) {
                try {
                    Connection conn = pool.fetchConnection(1000);
                    if (conn != null){
                        try {
                            conn.createStatement();
                            conn.commit();
                        } catch (SQLException e) {
                            e.printStackTrace();
                        } finally {
                            pool.closeConnection(conn);
                            got.incrementAndGet();
                        }
                    } else {
                        notGot.incrementAndGet();
                        System.out.println(Thread.currentThread().getName() + "等待超時(shí)");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    count--;
                }
            }
            countDownLatch.countDown();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        countDownLatch = new CountDownLatch(threadCount);
        AtomicLong got = new AtomicLong();
        AtomicLong notGot = new AtomicLong();
        for (int i = 0; i < threadCount; i++) {
            Thread t = new Thread(new GetConnectionThread(getConnCount, got, notGot), "DBFactory" + i);
            t.start();
        }
        countDownLatch.await();
        System.out.println("總共嘗試了: " + (threadCount * getConnCount));
        System.out.println("拿到連接的次數(shù):  " + got);
        System.out.println("沒能連接的次數(shù): " + notGot);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末羡宙,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子掐隐,更是在濱河造成了極大的恐慌狗热,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件虑省,死亡現(xiàn)場(chǎng)離奇詭異匿刮,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)探颈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門熟丸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人伪节,你說我怎么就攤上這事光羞。” “怎么了怀大?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵纱兑,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我化借,道長(zhǎng)潜慎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任蓖康,我火速辦了婚禮铐炫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘钓瞭。我一直安慰自己驳遵,他們只是感情好淫奔,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布山涡。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鸭丛。 梳的紋絲不亂的頭發(fā)上竞穷,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音鳞溉,去河邊找鬼瘾带。 笑死,一個(gè)胖子當(dāng)著我的面吹牛熟菲,可吹牛的內(nèi)容都是我干的看政。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼抄罕,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼允蚣!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起呆贿,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤嚷兔,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后做入,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冒晰,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年竟块,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了壶运。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡浪秘,死狀恐怖前弯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情秫逝,我是刑警寧澤恕出,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站违帆,受9級(jí)特大地震影響浙巫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜刷后,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一的畴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧尝胆,春花似錦丧裁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽二庵。三九已至,卻和暖如春缓呛,著一層夾襖步出監(jiān)牢的瞬間催享,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國(guó)打工哟绊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留因妙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓票髓,卻偏偏與公主長(zhǎng)得像攀涵,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子洽沟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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

  • 本文是我自己在秋招復(fù)習(xí)時(shí)的讀書筆記汁果,整理的知識(shí)點(diǎn),也是為了防止忘記玲躯,尊重勞動(dòng)成果据德,轉(zhuǎn)載注明出處哦!如果你也喜歡跷车,那...
    波波波先森閱讀 11,235評(píng)論 4 56
  • 本文首發(fā)于我的個(gè)人博客:尾尾部落 本文是我刷了幾十篇一線互聯(lián)網(wǎng)校招java后端開發(fā)崗位的面經(jīng)后總結(jié)的多線程相關(guān)題目...
    繁著閱讀 1,987評(píng)論 0 7
  • 一棘利、進(jìn)程和線程 進(jìn)程 進(jìn)程就是一個(gè)執(zhí)行中的程序?qū)嵗總€(gè)進(jìn)程都有自己獨(dú)立的一塊內(nèi)存空間朽缴,一個(gè)進(jìn)程中可以有多個(gè)線程善玫。...
    阿敏其人閱讀 2,608評(píng)論 0 13
  • 張懸是大海雷光夏是星空陳綺貞是太陽曹方是風(fēng)陳粒是光和火陳珊妮是煙和灰塵
    木卯丁閱讀 153評(píng)論 0 2
  • 1. a.將cell及它的子控件設(shè)置為不透明的。b.盡量少用或不用透明圖層密强。c.減少子控件的數(shù)量茅郎。d.盡量少用ad...
    BEYOND黃閱讀 1,121評(píng)論 0 7