一、在研究join的用法之前,先明確兩件事情。
1.join方法定義在Thread類(lèi)中,則調(diào)用者必須是一個(gè)線程,
例如:
- Thread t = new CustomThread();
-
- t.start();
-
- t.join();
2.上面的兩行代碼也是在一個(gè)線程里面執(zhí)行的。
以上出現(xiàn)了兩個(gè)線程,一個(gè)是我們自定義的線程類(lèi),我們實(shí)現(xiàn)了run方法,做一些我們需要的工作;另外一個(gè)線程,生成我們自定義線程類(lèi)的對(duì)象,然后執(zhí)行
- customThread.start();
-
- customThread.join();
在這種情況下,兩個(gè)線程的關(guān)系是一個(gè)線程由另外一個(gè)線程生成并起動(dòng),所以我們暫且認(rèn)為第一個(gè)線程叫做“子線程”,另外一個(gè)線程叫做“主線程”。
二、為什么要用join()方法
主線程生成并起動(dòng)了子線程,而子線程里要進(jìn)行大量的耗時(shí)的運(yùn)算(這里可以借鑒下線程的作用),當(dāng)主線程處理完其他的事務(wù)后,需要用到子線程的處理結(jié)果,這個(gè)時(shí)候就要用到j(luò)oin();方法了。
三、join方法的作用
在網(wǎng)上看到有人說(shuō)“將兩個(gè)線程合并”。這樣解釋我覺(jué)得理解起來(lái)還更麻煩。不如就借鑒下API里的說(shuō)法:
“等待該線程終止。”
解釋一下,是主線程(我在“一”里已經(jīng)命名過(guò)了)等待子線程的終止。也就是在子線程調(diào)用了join()方法后面的代碼,只有等到子線程結(jié)束了才能執(zhí)行。(Waits for this thread to die.)
四、用實(shí)例來(lái)理解
寫(xiě)一個(gè)簡(jiǎn)單的例子來(lái)看一下join()的用法,一共三個(gè)類(lèi):
1.CustomThread 類(lèi)
2. CustomThread1類(lèi)
3. JoinTestDemo 類(lèi),main方法所在的類(lèi)。
代碼1:
- package wxhx.csdn2;
-
-
-
-
-
- class CustomThread1 extends Thread {
- public CustomThread1() {
- super("[CustomThread1] Thread");
- };
- public void run() {
- String threadName = Thread.currentThread().getName();
- System.out.println(threadName + " start.");
- try {
- for (int i = 0; i < 5; i++) {
- System.out.println(threadName + " loop at " + i);
- Thread.sleep(1000);
- }
- System.out.println(threadName + " end.");
- } catch (Exception e) {
- System.out.println("Exception from " + threadName + ".run");
- }
- }
- }
- class CustomThread extends Thread {
- CustomThread1 t1;
- public CustomThread(CustomThread1 t1) {
- super("[CustomThread] Thread");
- this.t1 = t1;
- }
- public void run() {
- String threadName = Thread.currentThread().getName();
- System.out.println(threadName + " start.");
- try {
- t1.join();
- System.out.println(threadName + " end.");
- } catch (Exception e) {
- System.out.println("Exception from " + threadName + ".run");
- }
- }
- }
- public class JoinTestDemo {
- public static void main(String[] args) {
- String threadName = Thread.currentThread().getName();
- System.out.println(threadName + " start.");
- CustomThread1 t1 = new CustomThread1();
- CustomThread t = new CustomThread(t1);
- try {
- t1.start();
- Thread.sleep(2000);
- t.start();
- t.join();
- } catch (Exception e) {
- System.out.println("Exception from main");
- }
- System.out.println(threadName + " end!");
- }
- }
打印結(jié)果:
main start.//main方法所在的線程起動(dòng),但沒(méi)有馬上結(jié)束,因?yàn)檎{(diào)用t.join();,所以要等到t結(jié)束了,此線程才能向下執(zhí)行。
[CustomThread1] Thread start.//線程CustomThread1起動(dòng)
[CustomThread1] Thread loop at 0//線程CustomThread1執(zhí)行
[CustomThread1] Thread loop at 1//線程CustomThread1執(zhí)行
[CustomThread] Thread start.//線程CustomThread起動(dòng),但沒(méi)有馬上結(jié)束,因?yàn)檎{(diào)用t1.join();,所以要等到t1結(jié)束了,此線程才能向下執(zhí)行。
[CustomThread1] Thread loop at 2//線程CustomThread1繼續(xù)執(zhí)行
[CustomThread1] Thread loop at 3//線程CustomThread1繼續(xù)執(zhí)行
[CustomThread1] Thread loop at 4//線程CustomThread1繼續(xù)執(zhí)行
[CustomThread1] Thread end. //線程CustomThread1結(jié)束了
[CustomThread] Thread end.// 線程CustomThread在t1.join();阻塞處起動(dòng),向下繼續(xù)執(zhí)行的結(jié)果
main end!//線程CustomThread結(jié)束,此線程在t.join();阻塞處起動(dòng),向下繼續(xù)執(zhí)行的結(jié)果。
修改一下代碼,得到代碼2:(這里只寫(xiě)出修改的部分)
- public class JoinTestDemo {
- public static void main(String[] args) {
- String threadName = Thread.currentThread().getName();
- System.out.println(threadName + " start.");
- CustomThread1 t1 = new CustomThread1();
- CustomThread t = new CustomThread(t1);
- try {
- t1.start();
- Thread.sleep(2000);
- t.start();
-
- } catch (Exception e) {
- System.out.println("Exception from main");
- }
- System.out.println(threadName + " end!");
- }
- }
-
打印結(jié)果:
main start. // main方法所在的線程起動(dòng),但沒(méi)有馬上結(jié)束,這里并不是因?yàn)閖oin方法,而是因?yàn)門(mén)hread.sleep(2000);
[CustomThread1] Thread start. //線程CustomThread1起動(dòng)
[CustomThread1] Thread loop at 0//線程CustomThread1執(zhí)行
[CustomThread1] Thread loop at 1//線程CustomThread1執(zhí)行
main end!// Thread.sleep(2000);結(jié)束,雖然在線程CustomThread執(zhí)行了t1.join();,但這并不會(huì)影響到其他線程(這里main方法所在的線程)。
[CustomThread] Thread start. //線程CustomThread起動(dòng),但沒(méi)有馬上結(jié)束,因?yàn)檎{(diào)用t1.join();,所以要等到t1結(jié)束了,此線程才能向下執(zhí)行。
[CustomThread1] Thread loop at 2//線程CustomThread1繼續(xù)執(zhí)行
[CustomThread1] Thread loop at 3//線程CustomThread1繼續(xù)執(zhí)行
[CustomThread1] Thread loop at 4//線程CustomThread1繼續(xù)執(zhí)行
[CustomThread1] Thread end. //線程CustomThread1結(jié)束了
[CustomThread] Thread end. // 線程CustomThread在t1.join();阻塞處起動(dòng),向下繼續(xù)執(zhí)行的結(jié)果
五、從源碼看join()方法
在CustomThread的run方法里,執(zhí)行了t1.join();,進(jìn)入看一下它的JDK源碼:
- public final void join() throws InterruptedException {
- n(0);
- }
- public final void join() throws InterruptedException {
- join(0);
- }
然后進(jìn)入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;
- }
- }
- }
-
-
-
-
-
-
-
-
-
- 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;
- }
- }
- }
單純從代碼上看,如果線程被生成了,但還未被起動(dòng),調(diào)用它的join()方法是沒(méi)有作用的。將直接繼續(xù)向下執(zhí)行,這里就不寫(xiě)代碼驗(yàn)證了。