多線程學(xué)習(xí)一

先看一張狀態(tài)圖(圖片來源于網(wǎng)絡(luò))

1.join

如圖中所示趟薄,一個線程調(diào)用join后,進(jìn)入阻塞狀態(tài)件甥。join的作用就是等待一個線程并暫停自己何恶,直到等待的那個線程結(jié)束為止:

    public static void main(String[] args) {
        Thread A = new Thread(){
            @Override
            public void run() {
                System.out.println("A start");
                try {
                    sleep(2000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println("A end");
            }
        };
        Thread B = new Thread(){
            @Override
            public void run() {
                System.out.println("B start");
                try {
                    A.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("B end");
            }
        };
        A.start();
        B.start();      
    }

測試結(jié)果:

第一次:
A start
B start
A end
B end
第二次:
B start
A start
A end
B end

由于B調(diào)用了join孽锥,所以無論B是否先執(zhí)行,都是等到A執(zhí)行完畢后再執(zhí)行到結(jié)束细层。

2.wait/notify

這兩個是配合使用的惜辑,而且這兩個方法是不Thread特有的,而是屬于Object疫赎,java中所有類頂級父類都是Object盛撑,所以所有類都有這兩個方法。他們出現(xiàn)的原因是虚缎,在對臨界資源訪問時需要加鎖撵彻,但有時候需要放棄已有的鎖,所以就出現(xiàn)了wait实牡,讓出當(dāng)前的鎖陌僵,讓其他申請鎖的線程得以執(zhí)行,另外線程執(zhí)行完之后需要調(diào)用notify创坞,使之前讓出的依舊持有鎖從而可以運(yùn)行碗短。我們先看一個沒有wait的例子

    Object lock = new Object();
    public static void main(String[] args) {
        Thread A = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("A start");
                synchronized (lock) {
                    System.out.println("A 1"); 
                    try {
                        Thread.currentThread().sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }               
                    System.out.println("A 2");
                    System.out.println("A 3");
                }
                System.out.println("A end");
            }
        });

        Thread B = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("B start");
                synchronized (lock) {
                    System.out.println("B 1");
                    System.out.println("B 2");
                    System.out.println("B 3");
                }
                System.out.println("B end");
            }
        });
        A.start();
        try {
            Thread.currentThread().sleep(500);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        B.start();  
    }

運(yùn)行結(jié)果

A start
A 1
B start
A 2
A 3
A end
B 1
B 2
B 3
B end

可見雖然B啟動了,但臨界區(qū)的鎖被A持有题涨,所以要等A臨界區(qū)代碼執(zhí)行完畢后再執(zhí)行B的鄰接區(qū)代碼偎谁,下面再看有wait的部分:

    public static void main(String[] args) {
        Object lock = new Object();
        Thread A = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("A start");
                synchronized (lock) {
                    System.out.println("A 1"); 
                    try {
                        lock.wait();
                    } catch (InterruptedException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                    try {
                        Thread.currentThread().sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }               
                    System.out.println("A 2");
                    System.out.println("A 3");
                }
                System.out.println("A end");
            }
        });

        Thread B = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("B start");
                synchronized (lock) {
                    System.out.println("B 1");
                    System.out.println("B 2");
                    System.out.println("B 3");

                    lock.notify(); 
                }
                System.out.println("B end");
            }
        });
        A.start();
        try {
            Thread.currentThread().sleep(500);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        B.start();  
    }

測試結(jié)果

A start
A 1
B start
B 1
B 2
B 3
B end
A 2
A 3
A end

可見A的確讓出了鎖資源,B得到執(zhí)行纲堵,最后B喚醒A巡雨,A繼續(xù)執(zhí)行。如果B執(zhí)行完之后沒有調(diào)用notify席函,A是不會繼續(xù)執(zhí)行的铐望,有疑問的可以試試。還有一個notifyAll方法茂附,功能類似正蛙,是喚醒多個wait的線程

3.CountdownLatch

這是一個類似計(jì)數(shù)器的類,用于一個線程等待一定數(shù)量的線程都達(dá)到某種條件后才執(zhí)行的場景营曼,示例

    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(3);

        new Thread(){
            @Override
            public void run() {
                System.out.println("A 開始等待");
                try {
                    countDownLatch.await();
                    System.out.println("A 等待結(jié)束");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();

        IntStream.of(1,2,3).forEach(i->{
            String name = "Thread"+i;
            new Thread(){
                @Override
                public void run() {
                    System.out.println(name + "開始執(zhí)行");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(name + "執(zhí)行完畢");
                    countDownLatch.countDown();
                }
            }.start();
        });
    }

結(jié)果:

A 開始等待
Thread2開始執(zhí)行
Thread1開始執(zhí)行
Thread3開始執(zhí)行
Thread2執(zhí)行完畢
Thread3執(zhí)行完畢
Thread1執(zhí)行完畢
A 等待結(jié)束

CountdownLatch的構(gòu)造方法需要傳入一個等待時數(shù)目乒验,在需要等待的線程中調(diào)用await方法,之后每當(dāng)一個線程執(zhí)行完畢或在適當(dāng)時候調(diào)用countDown蒂阱,將計(jì)數(shù)器減一锻全,減到0時狂塘,原來等待的線程就可以執(zhí)行了

4.CyclicBarrier

這是一個類似于計(jì)數(shù)器的類,當(dāng)一定數(shù)量的線程都達(dá)到某種狀態(tài)時鳄厌,這些線程才可以繼續(xù)執(zhí)行睹耐,示例:

    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
        Random random = new Random();
        IntStream.of(1,2,3).forEach(i -> {
            String name = "Thread" + i;
            new Thread(){
                @Override
                public void run() {
                    setName(name);
                    System.out.println(getName() + "開始準(zhǔn)備");
                    try {
                        Thread.sleep((long) (Math.random() * 1000));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(getName()+"準(zhǔn)備完畢");
                    try {
                        cyclicBarrier.await();
                    } catch (Exception e) {
                        e.printStackTrace();
                    } 

                    System.out.println("所有線程準(zhǔn)備完畢" + getName() + "開始執(zhí)行");
                }
            }.start();
        });
    }

運(yùn)行結(jié)果:

Thread2開始準(zhǔn)備
Thread1開始準(zhǔn)備
Thread3開始準(zhǔn)備
Thread2準(zhǔn)備完畢
Thread3準(zhǔn)備完畢
Thread1準(zhǔn)備完畢
所有線程準(zhǔn)備完畢Thread1開始執(zhí)行
所有線程準(zhǔn)備完畢Thread3開始執(zhí)行
所有線程準(zhǔn)備完畢Thread2開始執(zhí)行

CyclicBarrier的構(gòu)造函數(shù)需要傳入等待時數(shù)量,然后每個線程在合適的時候調(diào)用await等待其他線程部翘,當(dāng)指定數(shù)量的線程都調(diào)用await后,所有線程一起開始執(zhí)行响委。
CyclicBarrier構(gòu)造還可以傳入一個Runnable新思,當(dāng)調(diào)用awit的線程達(dá)到一定數(shù)量時,會挑選一個線程率先執(zhí)行這個Runnable.如下:

    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3,()->System.out.println(Thread.currentThread().getName() + "+++"));
        Random random = new Random();
        IntStream.of(1,2,3).forEach(i -> {
            String name = "Thread" + i;
            new Thread(){
                @Override
                public void run() {
                    setName(name);
                    System.out.println(getName() + "開始準(zhǔn)備");
                    try {
                        Thread.sleep((long) (Math.random() * 1000));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(getName()+"準(zhǔn)備完畢");
                    try {
                        cyclicBarrier.await();
                    } catch (Exception e) {
                        e.printStackTrace();
                    } 

                    System.out.println("所有線程準(zhǔn)備完畢" + getName() + "開始執(zhí)行");
                }
            }.start();
        });
    }

Thread1開始準(zhǔn)備
Thread3開始準(zhǔn)備
Thread2開始準(zhǔn)備
Thread1準(zhǔn)備完畢
Thread2準(zhǔn)備完畢
Thread3準(zhǔn)備完畢
Thread3+++
所有線程準(zhǔn)備完畢Thread1開始執(zhí)行
所有線程準(zhǔn)備完畢Thread3開始執(zhí)行
所有線程準(zhǔn)備完畢Thread2開始執(zhí)行

5.Semaphore

類似于我們學(xué)過的進(jìn)程同步的PV操作赘风,看一個生產(chǎn)者消費(fèi)者的例子:

    public static void main(String[] args) throws InterruptedException {
        Semaphore apple = new Semaphore(0);
        Semaphore plate = new Semaphore(1);
        Thread Producer = new Thread(){
            @Override
            public void run() {
                // TODO Auto-generated method stub
                super.run();
                try {
                    System.out.println("申請盤子");
                    plate.acquire();
                    System.out.println("獲取盤子");
                    System.out.println("生成蘋果");
                    apple.release();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        };
        Thread Consumer = new Thread(){
            @Override
            public void run() {
                // TODO Auto-generated method stub
                super.run();
                try {
                    System.out.println("申請?zhí)O果");
                    apple.acquire();
                    System.out.println("獲取蘋果");
                    System.out.println("釋放盤子");
                    plate.release();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        };
        Consumer.start();
        Thread.sleep(1000);
        Producer.start();
    }

申請?zhí)O果
申請盤子
獲取盤子
生成蘋果
獲取蘋果
釋放盤子

用起來很簡單夹囚,acquire申請一個資源,release釋放一個邀窃,同樣也可以選擇調(diào)用重載方法荸哟,一次申請或釋放多個,構(gòu)造方法也可以傳入一個布爾類型變量瞬捕,表示是否公平分配(若為false鞍历,表示多個線程競爭一個資源時,誰等的時間長肪虎,誰優(yōu)先)劣砍。另外acquire為阻塞方法,一個線程可能無限等待扇救,所有有非阻塞的方法tryAcquire刑枝,調(diào)用availablePermits檢測當(dāng)前可用資源數(shù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末迅腔,一起剝皮案震驚了整個濱河市装畅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌沧烈,老刑警劉巖掠兄,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異掺出,居然都是意外死亡徽千,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門汤锨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來双抽,“玉大人,你說我怎么就攤上這事闲礼‰剐冢” “怎么了铐维?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長慎菲。 經(jīng)常有香客問我嫁蛇,道長,這世上最難降的妖魔是什么露该? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任睬棚,我火速辦了婚禮,結(jié)果婚禮上解幼,老公的妹妹穿的比我還像新娘抑党。我一直安慰自己,他們只是感情好撵摆,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布底靠。 她就那樣靜靜地躺著,像睡著了一般特铝。 火紅的嫁衣襯著肌膚如雪暑中。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天鲫剿,我揣著相機(jī)與錄音鳄逾,去河邊找鬼。 笑死牵素,一個胖子當(dāng)著我的面吹牛严衬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播笆呆,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼请琳,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了赠幕?” 一聲冷哼從身側(cè)響起俄精,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎榕堰,沒想到半個月后竖慧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逆屡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年圾旨,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片魏蔗。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡砍的,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出莺治,到底是詐尸還是另有隱情廓鞠,我是刑警寧澤帚稠,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站床佳,受9級特大地震影響滋早,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜砌们,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一杆麸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧浪感,春花似錦角溃、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽匆瓜。三九已至赢笨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間驮吱,已是汗流浹背茧妒。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留左冬,地道東北人桐筏。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像拇砰,于是被迫代替她去往敵國和親梅忌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344