如何保證線程的順序執行


問題:

現在有T1、T2、T3三個線程,你怎樣保證T2在T1執行完之后執行,T3在T2執行完之后執行?

方法1:使用join

使用Thread原生方法join,join方法是使所屬的線程對象x正常執行run()方法中的任務,而當前線程進行無限的阻塞,等到線程x執行完成后再繼續執行當前線程后面的代碼。

public static void main(String[] args) { final Thread T1 = new Thread(new Runnable() { public void run() { System.out.println("T1 run"); } }); final Thread T2 = new Thread(new Runnable() { public void run() { System.out.println("T2 run"); try{ T1.join(); }catch (Exception e){ e.printStackTrace(); } System.out.println("T2 run end"); } }); Thread T3 = new Thread(new Runnable() { public void run() { System.out.println("T3 run"); try{ T2.join(); }catch (Exception e){ e.printStackTrace(); } System.out.println("T3 run end"); } }); T1.start(); T2.start(); T3.start(); }

 

方法2:使用線程間通信的等待/通知機制

wait()方法是Object類的方法,該方法用來將當前線程置入“預執行隊列”中,並且在wait()所在的代碼行處停止執行,直到接到通知或被中斷為止。

在調用wait之前,線程必須獲取到該對象的對象級別鎖,即只能在同步方法或同步塊中調用wait()方法。在執行wait()方法后,當前線程釋放鎖。

private static boolean T2Run = false; //標識位,用來通知T2線程執行

    private static boolean T3Run = false; public static void main(String[] args) { Object lock1 = new Object(); Object lock2 = new Object(); Thread T1 = new Thread(new Runnable() { @Override public void run() { synchronized (lock1){ System.out.println("T1 run"); //t1 線程通知t2執行
                    T2Run = true; lock1.notify(); } } }); Thread T2 = new Thread(new Runnable() { @Override public void run() { synchronized (lock1){ if(!T2Run){ System.out.println("T2 wait"); try { lock1.wait(); } catch (Exception e){ e.printStackTrace(); } } System.out.println("T2 run"); //t2 線程通知t3執行
                    synchronized (lock2){ T3Run = true; lock2.notify(); } } } }); Thread T3 = new Thread(new Runnable() { @Override public void run() { synchronized (lock2){ if (!T3Run){ System.out.println("T3 wait"); try { lock2.wait(); } catch (Exception e){ e.printStackTrace(); } } System.out.println("T3 run"); } } }); T1.start(); T2.start(); T3.start(); }

 

方法3:使用Conditon

關鍵字synchronized與wait和notify/notifyAll方法相結合可以實現等待/通知模式,類ReetrantLock也可以實現同樣的功能,但需要借助於Condition對象。Condition可以實現多路通知,也就是在一個Lock對象里面可以創建多個Condition(即對象監視器)實例,線程對象可以注冊在指定的Condition中,從而可以有選擇性的進行線程通知,在調度線程上更加靈活。在使用notify/notifyAll通知時,被通知的線程卻是由JVM隨機選擇的。

/** * 使用condition */
    private Lock lock = new ReentrantLock(); private Condition condition2 = lock.newCondition(); private Condition condition3 = lock.newCondition(); private static Boolean t2Run = false; private static Boolean t3Run = false; private void useCondition(){ Thread T1 = new Thread(new Runnable() { @Override public void run() { lock.lock(); //獲取鎖
                System.out.println("T1 run"); t2Run = true; //設置t2可以運行
                System.out.println("T1 run finish signal T2"); condition2.signal(); //通知T2執行
                lock.unlock(); //解鎖當前線程
                System.out.println("T1 unlock"); } }); Thread T2 = new Thread(new Runnable() { @Override public void run() { lock.lock(); try{ if (!t2Run){ condition2.await(); //如果是false ,則等待
 } //若是true,則代表T2可以執行
                    System.out.println("T2 run"); t3Run = true; condition3.signal(); System.out.println("T2 run finish signal T3"); }catch (Exception e){ e.printStackTrace(); } finally { lock.unlock(); } } }); Thread T3 = new Thread(new Runnable() { @Override public void run() { lock.lock(); try{ if (!t3Run){ condition3.await(); //如果是false ,則等待
 } //若是true,則代表T2可以執行
                    System.out.println("T3 run"); }catch (Exception e){ e.printStackTrace(); } finally { lock.unlock(); } } }); T1.start(); T2.start(); T3.start(); }

 

方法4:使用線程池

使用newSingleThreadExecutor線程池,由於核心線程數只有一個,所以能夠順序執行。

/** * 線程池 * 核心線程數:1 * 最大線程數:1 * 在日常中不建議使用newSingleThreadExecutor,因為阻塞隊列個數沒有限制,會導致內存溢出 * */
    static ExecutorService executorService = Executors.newSingleThreadExecutor(); public static void main(String[] args) { Thread T1 = new Thread(new Runnable() { @Override public void run() { System.out.println("T1 run"); } }); Thread T2 = new Thread(new Runnable() { @Override public void run() { System.out.println("T2 run"); } }); Thread T3 = new Thread(new Runnable() { @Override public void run() { System.out.println("T3 run"); } }); executorService.submit(T1); executorService.submit(T2); executorService.submit(T3); executorService.shutdown(); }

 

方法5:使用線程的CountDownLatch

CountDownLatch 的作用是:當一個線程需要另外一個或多個線程完成后,再開始執行。比如主線程要等待一個子線程完成環境相關配置的加載工作,主線程才繼續執行,就可以利用 CountDownLatch 來實現。

比較重要的方法:

CountDownLatch(int count); //構造方法,創建一個值為count 的計數器

await();//阻塞當前線程,將當前線程加入阻塞隊列。

countDown();//對計數器進行遞減1操作,當計數器遞減至0時,當前線程會去喚醒阻塞隊列里的所有線程。

/**
     * 計數器1 用於T1線程通知T2線程
     * 注意:這里個數都設置成立1 ,當T1執行完成后,執行countDown,來通知T2線程
     */
    static CountDownLatch countDownLatch1 = new CountDownLatch(1);

    /**
     * 計數器2 用於T2線程通知T3線程
     */
    static CountDownLatch countDownLatch2 = new CountDownLatch(1);

    public static void main(String[] args) {
        Thread T1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("T1 run");
                countDownLatch1.countDown();
                System.out.println("T1 countDown finish");
            }
        });
        Thread T2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    countDownLatch1.await();
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("T2 run");
                countDownLatch2.countDown();
            }
        });
        Thread T3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    countDownLatch2.await();
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("T3 run");
            }
        });
    }

 

方法6:使用cyclicbarrier (多個線程互相等待,直到到達同一個同步點,再繼續一起執行

比較重要的方法 

CyclicBarrier(int parties) //構造方法,參數表示攔截的線程的個數

CyclicBarrier(int parties, Runnable barrierAction) //也是構造方法,可以通過后面的參數,這是線程的優先級

await() //告訴CyclicBarrier自己已經到達同步點,然后當前線程被阻塞,當所有線程都到達同步點(barrier)時,喚醒所有的等待線程,一起往下繼續運行,可根據參數barrierAction決定優先執行的線程

 

/**
     * 設置2個線程互相等待,直到到達同一個同步點,再繼續一起執行。T1不執行完,T2就永遠不會執行
     */
    static CyclicBarrier cyclicBarrier1 = new CyclicBarrier(2);

    /**
     * 設置2個線程互相等待,直到到達同一個同步點,再繼續一起執行。
     */
    static CyclicBarrier cyclicBarrier2 = new CyclicBarrier(2);

    public static void main(String[] args) {
        Thread T1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("T1 run");
                try{
                    cyclicBarrier1.await();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        });
        Thread T2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    cyclicBarrier1.await();
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("T2 run");
                try{
                    cyclicBarrier2.await();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        });
        Thread T3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    cyclicBarrier2.await();
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("T3 run");
            }
        });

        T1.start();;
        T2.start();
        T3.start();
    }

 

方法7:使用信號量 Semaphore

Semaphore計數信號量,常用於限制可以訪問某些資源(物理或邏輯的)線程數目。

常用的方法:

Semaphore(int permits);//構造方法,permits就是允許同時運行的線程數目

public Semaphore(int permits,boolean fair);//permits就是允許同時運行的線程數目 ,fair 是否為公平鎖,如果是公平鎖,那么獲得鎖的順序與線程啟動順序有關

void acquire()// 從此信號量獲取一個許可,在提供一個許可前一直將線程阻塞,否則線程被中斷。

tryAcquire() //嘗試獲得令牌,返回獲取令牌成功或失敗,不阻塞線程

release() //釋放一個令牌,喚醒一個獲取令牌不成功的阻塞線程。

/**
     * 設置信號量初始值為0 ,讓T1 把信號量+1,這樣,T2就可以執行了
     */
    static Semaphore semaphore1 = new Semaphore(0);

    static Semaphore semaphore2 = new Semaphore(0);

    public static void main(String[] args) {
        Thread T1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("T1 run");
                try{
                    semaphore1.release();
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("T1 semaphore1 + 1");
            }
        });
        Thread T2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    semaphore1.acquire();
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("T2 semaphore2 + 1");
                try{
                    semaphore2.release();
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("T2 run");
            }
        });
        Thread T3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    semaphore2.acquire();
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("T3 run");
            }
        });

        T1.start();;
        T2.start();
        T3.start();
    }

 

 總結:共有7中方法

  1. 使用Thread原生方法join

  2. 使用線程間通信的等待/通知機制

  3. 使用Conditon

  4. 使用線程池

  5. 使用線程的CountDownLatch

  6. 使用cyclicbarrier (多個線程互相等待,直到到達同一個同步點,再繼續一起執行
  7. 使用信號量 Semaphore

 

參考:

https://www.cnblogs.com/wenjunwei/p/10573289.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM