摘要:今天,咱們就一塊兒來看看線程究竟是如何執行的,它的順序又是怎樣的?
本文分享自華爲雲社區《線程的執行順序與你想的不同!!》,做者:冰 河 。測試
1、線程的執行順序是不肯定的
調用Thread的start()方法啓動線程時,線程的執行順序是不肯定的。也就是說,在同一個方法中,連續建立多個線程後,調用線程的start()方法的順序並不能決定線程的執行順序。url
例如,這裏,看一個簡單的示例程序,以下所示。spa
package io.binghe.concurrent.lab03; /** * @author binghe * @version 1.0.0 * @description 線程的順序,直接調用Thread.start()方法執行不能確保線程的執行順序 */ public class ThreadSort01 { public static void main(String[] args){ Thread thread1 = new Thread(() -> { System.out.println("thread1"); }); Thread thread2 = new Thread(() -> { System.out.println("thread2"); }); Thread thread3 = new Thread(() -> { System.out.println("thread3"); }); thread1.start(); thread2.start(); thread3.start(); } }
在ThreadSort01類中分別建立了三個不一樣的線程,thread一、thread2和thread3,接下來,在程序中按照順序分別調用thread1.start()、thread2.start()和thread3.start()方法來分別啓動三個不一樣的線程。.net
那麼,問題來了,線程的執行順序是否按照thread一、thread2和thread3的順序執行呢?運行ThreadSort01的main方法,結果以下所示。線程
thread1 thread2 thread3
再次運行時,結果以下所示。code
thread1 thread3 thread2
第三次運行時,結果以下所示。blog
thread2 thread3 thread1
能夠看到,每次運行程序時,線程的執行順序可能不一樣。線程的啓動順序並不能決定線程的執行順序。ip
2、如何確保線程的執行順序
1.確保線程執行順序的簡單示例
在實際業務場景中,有時,後啓動的線程可能須要依賴先啓動的線程執行完成才能正確的執行線程中的業務邏輯。此時,就須要確保線程的執行順序。那麼如何確保線程的執行順序呢?get
能夠使用Thread類中的join()方法來確保線程的執行順序。例如,下面的測試代碼。it
package io.binghe.concurrent.lab03; /** * @author binghe * @version 1.0.0 * @description 線程的順序,Thread.join()方法可以確保線程的執行順序 */ public class ThreadSort02 { public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(() -> { System.out.println("thread1"); }); Thread thread2 = new Thread(() -> { System.out.println("thread2"); }); Thread thread3 = new Thread(() -> { System.out.println("thread3"); }); thread1.start(); //實際上讓主線程等待子線程執行完成 thread1.join(); thread2.start(); thread2.join(); thread3.start(); thread3.join(); } }
能夠看到,ThreadSort02類比ThreadSort01類,在每一個線程的啓動方法下面添加了調用線程的join()方法。此時,運行ThreadSort02類,結果以下所示。
thread1 thread2 thread3
再次運行時,結果以下所示。
thread1 thread2 thread3
第三次運行時,結果以下所示。
thread1 thread2 thread3
能夠看到,每次運行的結果都是相同的,因此,使用Thread的join()方法可以保證線程的前後執行順序。
2.join方法如何確保線程的執行順序
既然Thread類的join()方法可以確保線程的執行順序,咱們就一塊兒來看看Thread類的join()方法究竟是個什麼鬼。
進入Thread的join()方法,以下所示。
public final void join() throws InterruptedException { join(0); }
能夠看到join()方法調用同類中的一個有參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; } } }
能夠看到,有一個long類型參數的join()方法使用了synchroinzed修飾,說明這個方法同一時刻只能被一個實例或者方法調用。因爲,傳遞的參數爲0,因此,程序會進入以下代碼邏輯。
if (millis == 0) { while (isAlive()) { wait(0); } }
首先,在代碼中以while循環的方式來判斷當前線程是否已經啓動處於活躍狀態,若是已經啓動處於活躍狀態,則調用同類中的wait()方法,並傳遞參數0。繼續跟進wait()方法,以下所示。
public final native void wait(long timeout) throws InterruptedException;
能夠看到,wait()方法是一個本地方法,經過JNI的方式調用JDK底層的方法來使線程等待執行完成。
須要注意的是,調用線程的wait()方法時,會使主線程處於等待狀態,等待子線程執行完成後再次向下執行。也就是說,在ThreadSort02類的main()方法中,調用子線程的join()方法,會阻塞main()方法的執行,當子線程執行完成後,main()方法會繼續向下執行,啓動第二個子線程,並執行子線程的業務邏輯,以此類推。