1. 程式人生 > 實用技巧 >現在有 T1、T2、T3 三個執行緒,怎樣保證 T2 在 T1 執行完後執行T3 在 T2 執行完

現在有 T1、T2、T3 三個執行緒,怎樣保證 T2 在 T1 執行完後執行T3 在 T2 執行完

問題:現在有T1、T2、T3三個執行緒,你怎樣保證T2在T1執行完後執行,T3在T2執行完後執行

實現:使用Thread中的join方法實現

分析:

  1. Thread類中的join方法是用來同步的,底層其實是呼叫了 wait方法。先來看一下演示程式碼:
package com.whh.concurrency;
/**
 *@description:
 * 問題:現在有 T1、T2、T3 三個執行緒,怎樣保證 T2 在 T1 執行完後執行T3在T2執行完
 * 分析:使用join方法實現
 *@author:wenhuohuo
 */
public class MyJoin {
    public static void main(String[] args) {
        final Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("執行緒1");
            }
        },"t1");
        final Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    t1.join();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("執行緒2");
            }
        },"t2");
        final Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    t2.join();
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("執行緒3");
            }
        },"t3");
        t3.start();
        t2.start();
        t1.start();
    }
}

執行結果:

執行緒3
執行緒2
執行緒1

可以看到,我們讓t2執行緒呼叫t1.join,t3呼叫t2.join,儘管是t3,t2,t1分別start,執行順序還是t1,t2,t3。是因為join方法底層使用的是wait方法。

  1. 檢視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()) { //isAlive()是native方法,判斷執行緒是否還存活
                wait(0);		//wait(0),不計時間,一直等待,直到有notify()或notifyAll()來喚醒。
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);//傳入時間,表示在時間值消耗完之前一直等待,直到過了等待時間。
                now = System.currentTimeMillis() - base;
            }
        }
    }

1)從原始碼中,我們結合之前的程式碼分析,t2.join()t3.join(),均沒有傳值,相當於join(0),表示不計時間,t2會一直wait等待t1執行完成,t3會一直wait等待t2執行完成。所以執行結果順序是t3,t2,t1。

2)當傳入的毫秒值不為0時,就一直迴圈等待,直到過了等待時間(dalay<=0),則執行break方法,那麼將不再等待。

  1. 改變join()傳入的毫秒值,檢視執行順序並分析結果:
public class MyJoin {
    public static void main(String[] args) {
        final Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //處理業務時間,模擬為8秒
                    Thread.sleep(8000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("執行緒1");
            }
        },"t1");
        final Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    t1.join(4000); //t2等待t1執行緒4秒
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("執行緒2");
            }
        },"t2");
        final Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    t2.join(2000); //t3等待t2執行緒2秒
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("執行緒3");
            }
        },"t3");
        t3.start();
        t2.start();
        t1.start();
    }
}

執行結果:

執行緒3	//程式啟動過了2秒執行t3
執行緒2 //過了4秒執行t2
執行緒1 //過了8秒執行t1

分析:我們讓t1 睡眠8秒模擬業務執行時間,t2等待t1 的時間為4秒,t3等待t2的時間為2秒。那麼當t1,t2,t3啟動後,等待的時間,t3會因為t2的等待時間4秒太長而先與t2執行,t2會因為t1的8秒太長而先與t1執行。