標籤:end exce 時間 row argument art comm ring lang
在很多情況下,主線程建立並啟動子線程,如果子線程中有大量的耗時運算,主線程將早於子線程結束,如果想讓主線程等待子線程結束後再結束,那麼我們可以使用join()方法。調用join()方法的意思是當前線程使調用了該方法的線程執行完成然後再執行自己本身。api文檔如下:
public final void join(long millis, int nanos) throws InterruptedExceptionWaits at most millis milliseconds plus nanos nanoseconds for this thread to die.This implementation uses a loop of this.wait calls conditioned on this.isAlive. As a thread terminates the this.notifyAll method is invoked. It is recommended that applications not use wait, notify, or notifyAll on Thread instances.Parameters:millis - the time to wait in millisecondsnanos - 0-999999 additional nanoseconds to waitThrows:IllegalArgumentException - if the value of millis is negative, or the value of nanos is not in the range 0-999999InterruptedException - if any thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown
簡單翻譯如下:調用該方法的線程會等待millils+nanos的時間,直到該線程執行完(調用該方法的Thread.isAlive()方法返回false時)。該方法的實現是使用迴圈判斷該線程isAlive()方法,如果為true,那麼就調用wait()方法進行等待。當線程結束時,notifyAll()方法被調用。如果調用join()後再調用notify()/notifyAll()則可能會時join()方法失效。源碼如下:
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; } } }
例子:
package soarhu;class Service{ void readMethod(){ try { for (int i = 0; i < 5; i++) { System.out.println(i); } }catch (Exception e){ e.printStackTrace(); } }}public class Test { public static void main(String[] args) throws Exception { Service o = new Service(); new Thread(){ @Override public void run() { o.readMethod(); } }.start(); System.out.println("main......."); }}
輸出結果:
main.......01234Process finished with exit code 0
結果分析:可知主線程方法先被執行完畢。如果要使子線程先執行完,然後執行主線程。那麼可以調用join()方法。
package soarhu;class Service{ void readMethod(){ try { for (int i = 0; i < 25; i++) { Thread.yield(); System.out.println(i+" "+Thread.currentThread().getName()); } }catch (Exception e){ e.printStackTrace(); } }}public class Test { public static void main(String[] args) throws Exception { Service o = new Service(); Thread t = new Thread(){ @Override public void run() { o.readMethod(); } }; t.start(); t.join(); for (int i = 0; i < 10; i++) { System.out.println("main......."+i); } }}
輸出結果:
0 Thread-01 Thread-02 Thread-03 Thread-04 Thread-05 Thread-06 Thread-07 Thread-08 Thread-09 Thread-010 Thread-011 Thread-012 Thread-013 Thread-014 Thread-015 Thread-016 Thread-017 Thread-018 Thread-019 Thread-020 Thread-021 Thread-022 Thread-023 Thread-024 Thread-0main.......0main.......1main.......2main.......3main.......4main.......5main.......6main.......7main.......8main.......9Process finished with exit code 0
可知確實是等待子線程執行完後才執行主線程。我們不用join()也可以參考api源碼來實現join().如下:
public class Test { public static void main(String[] args) throws Exception { Service o = new Service(); Thread t = new Thread() { @Override public void run() { o.readMethod(); } }; t.start(); //t.join(); synchronized (t) { do { t.wait(); } while (t.isAlive()); } for (int i = 0; i < 10; i++) { Thread.yield(); System.out.println("main......." + i); } }}
輸出結果:
0 Thread-01 Thread-02 Thread-03 Thread-04 Thread-05 Thread-06 Thread-07 Thread-08 Thread-09 Thread-010 Thread-011 Thread-012 Thread-013 Thread-014 Thread-015 Thread-016 Thread-017 Thread-018 Thread-019 Thread-020 Thread-021 Thread-022 Thread-023 Thread-024 Thread-0main.......0main.......1main.......2main.......3main.......4main.......5main.......6main.......7main.......8main.......9Process finished with exit code 0
可見join()方法的實現方式很簡單,就是加一個同步方法,再內部迴圈判斷該線程是否結束,如果未結束,則再jon()這個地方一致阻塞,導致下面的代碼不能被執行,如果在調用join()方法後,再調用該線程的wait()方法則會發生死結,調用notify()則可能發生死結或者join()失效。例子如下:
package soarhu;import java.util.concurrent.TimeUnit;class Service { void readMethod() { try { for (int i = 0; i < 25; i++) { TimeUnit.MILLISECONDS.sleep(300); Thread.yield(); while (i==5){ //1 line synchronized (this){ //wait(); // 發生死結 this.notifyAll(); } } System.out.println(i + " " + Thread.currentThread().getName()); } } catch (Exception e) { e.printStackTrace(); } }}public class Test { public static void main(String[] args) { Service o = new Service(); Thread t = new Thread() { @Override public void run() { o.readMethod(); } }; t.start(); try { t.join(); for (int i = 0; i < 10; i++) { System.out.println("main......." + i); } } catch (InterruptedException e) { e.printStackTrace(); } }}
輸出結果:死結
0 Thread-01 Thread-02 Thread-03 Thread-04 Thread-0
由於join()內部是用的wait()方法,那麼也可能會拋出中斷異常,舉例如下:
package soarhu;import java.util.concurrent.TimeUnit;class Service extends Thread { @Override public void run() { try { for (int i = 0; i < 25; i++) { TimeUnit.MILLISECONDS.sleep(300); Thread.yield(); if (i==5){ this.interrupt(); } System.out.println(i + " " + Thread.currentThread().getName()); } } catch (Exception e) { e.printStackTrace(); } }}public class Test { public static void main(String[] args) { Service o = new Service(); o.start(); try { o.join(); System.out.println("isAlive? "+o.isAlive()); for (int i = 0; i < 10; i++) { System.out.println("main......." + i); } } catch (InterruptedException e) { e.printStackTrace(); } }}
輸出結果:
0 Thread-01 Thread-02 Thread-03 Thread-04 Thread-0java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:340)5 Thread-0 at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)isAlive? false at soarhu.Service.run(Test.java:12)main.......0main.......1main.......2main.......3main.......4main.......5main.......6main.......7main.......8main.......9Process finished with exit code 0
java多線程基本概述(七)——join()方法