Java技術(shù)專題-帶你理解Thread的join方法

前提介紹

  1. 任何對(duì)象都可以作為鎖對(duì)象,鎖對(duì)象的行為都是一樣的嗎?之前我一直認(rèn)為鎖對(duì)象的方法都是定義在Object類中,而所有類都是Object的子類玫坛,這些方法又都是native方法,那么用哪個(gè)對(duì)象作為鎖對(duì)象又有什么區(qū)別呢包晰?

  2. 一個(gè)線程對(duì)象a在run()方法內(nèi)部調(diào)用線程對(duì)象b的join()方法湿镀,那么是將b線程加入,等到b線程執(zhí)行完畢再執(zhí)行a線程伐憾?那么如果還有一個(gè)正在執(zhí)行的c線程呢勉痴,線程c也會(huì)等待b執(zhí)行完嗎?

代碼1:

public class Application1 {

    public static void main(String[] args) {
        Thread prepare =new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println("Hello,World!-----" + i);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        prepare.start();
        System.out.println("Hello,ZBY!");
    }
}

控制臺(tái)輸出:

Hello,ZBY!

Hello,World!——0

Hello,World!——1

Hello,World!——2

Hello,World!——3

Hello,World!-----4

結(jié)果不難分析树肃,主線程直接執(zhí)行蒸矛,而prepare線程雖然啟動(dòng)了,但是執(zhí)行沒那么快胸嘴,所以后執(zhí)行了雏掠。但是我的prepare是進(jìn)行準(zhǔn)備工作的,我想讓prepare線程執(zhí)行完畢后再執(zhí)行主線程劣像。

代碼2:


public class Application2 {
    public static void main(String[] args) {
        Thread prepare =new Thread(new Runnable() {
            public void run() {
                for(int i = 0; i < 5; i++) {
                    System.out.println("Hello,World!-----" + i);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        prepare.start();
        try {
            prepare.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Hello,ZBY!");
    }
}

控制臺(tái)輸出:

Hello,World!——0
Hello,World!——1
Hello,World!-----2
Hello,World!-----3
Hello,World!-----4
Hello,ZBY!

加了一個(gè)一行代碼:prepare.join()乡话;要是之前我會(huì)理解成把prepare加入到主線程先執(zhí)行,執(zhí)行完才能執(zhí)行其它線程耳奕。然而绑青,非也。

Thread.join() 源代碼:

public final void join() throws InterruptedException {
        join(0);
}

public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;
        if(millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }
        if(millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if(delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
       }
   }
}

本質(zhì)分析

這兒可以看出來屋群,我們調(diào)用prepare.join()的時(shí)候發(fā)生了什么:把prepare作為鎖對(duì)象闸婴,調(diào)用鎖對(duì)象的wait(0)方法,阻塞當(dāng)前線程芍躏!邪乍,也就是說,并不是把需要執(zhí)行的線程加入進(jìn)來讓他先執(zhí)行,而是阻塞當(dāng)前的線程溺欧!**

特殊情況

那么,如果還有第三個(gè)線程也在執(zhí)行柏肪,那么prepare線程是不會(huì)一直執(zhí)行姐刁,而是跟第三個(gè)線程搶CPU執(zhí)行權(quán)。**總結(jié)起來就是烦味,其實(shí)就是阻塞了當(dāng)前的線程聂使,對(duì)于其他線程都是沒有影響的。感興趣可以加入第三個(gè)線程自己測(cè)試一下谬俄。

代碼3:

public class Application3 {
    public static void main(String[] args) {
        Thread prepare =new Thread(new Runnable() {
            public void run() {
                for(int i = 0; i < 5; i++) {
                    System.out.println("Hello,World!-----" + i);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        prepare.start();
        synchronized(prepare){
            try {
                prepare.wait(0);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("Hello,ZBY!");
    }
}

控制臺(tái)輸出:

Hello,World!——0
Hello,World!——1
Hello,World!-----2
Hello,World!-----3
Hello,World!-----4
Hello,ZBY!
  • 這兒就解決了第二個(gè)問題柏靶,join()方法其實(shí)不是把調(diào)用的線程加入進(jìn)來優(yōu)先執(zhí)行,而是阻塞當(dāng)前線程溃论!,并且通過加鎖的線程進(jìn)行鎖住屎蜓。

  • 看完代碼3就有疑問了,prepare.wait(0);自然沒錯(cuò)钥勋,阻塞住了主線程炬转。但是并沒有任何地方調(diào)用notify或者notifyAll方法,線程不是應(yīng)該一直阻塞么算灸,怎么會(huì)在prepare執(zhí)行完后繼續(xù)執(zhí)行主線程代碼了扼劈?

  • 這就是第一個(gè)問題了,普通對(duì)象當(dāng)然是wait后必須等待notify喚醒才能繼續(xù)執(zhí)行菲驴,但是Thread對(duì)象呢荐吵?

  • 具體的我也不知道,但是從這兒可以推論出赊瞬,thread對(duì)象在執(zhí)行完畢后先煎,自動(dòng)喚醒了!那么到底是notify還是notifyAll呢巧涧?那么榨婆,多啟動(dòng)一個(gè)線程,并使用prepare對(duì)象作為鎖對(duì)象褒侧,調(diào)用wait方法良风。

代碼4:


public class Application3 {
    public static void main(String[] args) {
        final Thread prepare =new Thread(new Runnable() {
            public void run() {
                for(int i = 0; i < 5; i++) {
                    System.out.println("Hello,World!-----" + i);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        prepare.start();
        Thread ready =new Thread(new Runnable() {
            public void run() {
                synchronized (prepare) {
                    try {
                        prepare.wait(0);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                for(int i = 0; i < 10; i++) {
                    System.out.println("Hello,Earth!-----" + i);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        ready.start();
        synchronized (prepare) {
            try {
                prepare.wait(0);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("Hello,ZBY!");
    }
}

控制臺(tái)輸出:

Hello,World!-----0
Hello,World!-----1
Hello,World!-----2
Hello,World!-----3
Hello,World!-----4
Hello,Earth!-----0
Hello,ZBY!
Hello,Earth!-----1
Hello,Earth!-----2
Hello,Earth!-----3
Hello,Earth!-----4
Hello,Earth!-----5
Hello,Earth!-----6
Hello,Earth!-----7
Hello,Earth!-----8
Hello,Earth!-----9

可以看出,在prepare執(zhí)行完之后闷供,自動(dòng)把ready和main線程都喚醒了烟央!也就是說,使用Thread對(duì)象作為鎖對(duì)象歪脏,在Thread執(zhí)行完成之后疑俭,會(huì)喚醒使用該對(duì)象作為鎖對(duì)象調(diào)用wait()休眠的線程。

總結(jié):

使用線程的join方法婿失,是將調(diào)用的線程對(duì)象作為鎖對(duì)象钞艇,阻塞當(dāng)前線程啄寡,不影響其他線程的運(yùn)行

推論

Thread對(duì)象作為線程鎖對(duì)象哩照,會(huì)在Thread對(duì)象執(zhí)行完后挺物,調(diào)用Thread對(duì)象的notifyAll方法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末飘弧,一起剝皮案震驚了整個(gè)濱河市识藤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌次伶,老刑警劉巖痴昧,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異冠王,居然都是意外死亡赶撰,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門柱彻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來扣囊,“玉大人,你說我怎么就攤上這事绒疗∏中” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵吓蘑,是天一觀的道長(zhǎng)惕虑。 經(jīng)常有香客問我,道長(zhǎng)磨镶,這世上最難降的妖魔是什么溃蔫? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮琳猫,結(jié)果婚禮上伟叛,老公的妹妹穿的比我還像新娘。我一直安慰自己脐嫂,他們只是感情好统刮,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著账千,像睡著了一般侥蒙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上匀奏,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天鞭衩,我揣著相機(jī)與錄音,去河邊找鬼。 笑死论衍,一個(gè)胖子當(dāng)著我的面吹牛瑞佩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播坯台,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼炬丸,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了捂人?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤矢沿,失蹤者是張志新(化名)和其女友劉穎滥搭,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捣鲸,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瑟匆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了栽惶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片愁溜。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖外厂,靈堂內(nèi)的尸體忽然破棺而出冕象,到底是詐尸還是另有隱情,我是刑警寧澤汁蝶,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布渐扮,位于F島的核電站,受9級(jí)特大地震影響掖棉,放射性物質(zhì)發(fā)生泄漏墓律。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一幔亥、第九天 我趴在偏房一處隱蔽的房頂上張望耻讽。 院中可真熱鬧,春花似錦帕棉、人聲如沸针肥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽祖驱。三九已至,卻和暖如春瞒窒,著一層夾襖步出監(jiān)牢的瞬間捺僻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留匕坯,地道東北人束昵。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像葛峻,于是被迫代替她去往敵國(guó)和親锹雏。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

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