java 多線程按照順序執行任務

1、多線程按照順序執行任務,方法如下: 

  • 使用線程的join方法
  • 使用主線程的join方法
  • 使用線程的線程池方法
  • 使用線程的CountDownLatch(倒計數)方法
  • 使用線程的CyclicBarrier(迴環柵欄)方法
  • 使用線程的Semaphore(信號量)方法

2.多線程併發執行,等全部執行完成後在繼續執行往下程序,方法如下:

  • 使用線程的CountDownLatch(倒計數)方法    ----具體看該篇文章有寫,這裏不具體詳細說明
  • 使用線程的CyclicBarrier(迴環柵欄)方法        ----具體看該篇文章有寫,這裏不具體詳細說明


我們下面需要完成這樣一個應用場景:

1.早上;2.測試人員、產品經理、開發人員陸續的來公司上班;3.產品經理規劃新需求;4.開發人員開發新需求功能;5.測試人員測試新功能。

規劃需求,開發需求新功能,測試新功能是一個有順序的,我們把thread1看做產品經理,thread2看做開發人員,thread3看做測試人員。

(1)、使用線程的join方法
join():是Theard的方法,作用是調用線程需等待該join()線程執行完成後,才能繼續向下運行。
應用場景當一個線程必須等待另一個線程執行完畢才能執行時可以使用join方法
 

public static void main(String[] args) {
    final Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("產品經理規劃新需求");
        }
    });

    final Thread thread2 = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                thread1.join();
                System.out.println("開發人員開發新需求功能");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });

    Thread thread3 = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                thread2.join();
                System.out.println("測試人員測試新功能");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });

    System.out.println("早上:");
    System.out.println("測試人員來上班了...");
    thread3.start();
    System.out.println("產品經理來上班了...");
    thread1.start();
    System.out.println("開發人員來上班了...");
    thread2.start();
}

輸出如下:
早上:
測試人員來上班了...
產品經理來上班了...
開發人員來上班了...
產品經理規劃新需求
開發人員開發新需求功能
測試人員測試新功能

2、使用主線程的join方法
這裏是在主線程中使用join()來實現對線程的阻塞。
 

public static void main(String[] args) {
    final Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("產品經理正在規劃新需求...");
        }
    });

    final Thread thread2 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("開發人員開發新需求功能");
        }
    });

    final Thread thread3 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("測試人員測試新功能");
        }
    });

    try {

        System.out.println("開發人員和測試人員休息中...");
        System.out.println("早上:");
        System.out.println("產品經理來上班了");
        System.out.println("測試人員來上班了");
        System.out.println("開發人員來上班了");
        thread1.start();
        //在父進程調用子進程的join()方法後,父進程需要等待子進程運行完再繼續運行。
        thread1.join();
        System.out.println("產品經理新需求規劃完成!");
        thread2.start();
        System.out.println("測試人員休息會...");
        thread2.join();
        thread3.start();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

輸出結果如下:
開發人員和測試人員休息中...
早上:
產品經理來上班了
測試人員來上班了
開發人員來上班了
產品經理正在規劃新需求...
產品經理新需求規劃完成!
測試人員休息會...
開發人員開發新需求功能
測試人員測試新功能

3、使用線程的線程池方法
JAVA通過Executors提供了四種線程池

  • 單線程化線程池(newSingleThreadExecutor);
  • 可控最大併發數線程池(newFixedThreadPool);
  • 可回收緩存線程池(newCachedThreadPool);
  • 支持定時與週期性任務的線程池(newScheduledThreadPool)。

單線程化線程池(newSingleThreadExecutor):優點,串行執行所有任務。

submit():提交任務。

shutdown():方法用來關閉線程池,拒絕新任務。

應用場景:串行執行所有任務。如果這個唯一的線程因爲異常結束,那麼會有一個新的線程來替代它。此線程池保證所有任務的執行順序按照任務的提交順序執行

 

static ExecutorService executorService = Executors.newSingleThreadExecutor();

public static void main(String[] args) throws Exception {

    final Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("產品經理規劃新需求");
        }
    });

    final Thread thread2 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("開發人員開發新需求功能");
        }
    });

    Thread thread3 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("測試人員測試新功能");
        }
    });

    System.out.println("早上:");
    System.out.println("產品經理來上班了");
    System.out.println("測試人員來上班了");
    System.out.println("開發人員來上班了");
    System.out.println("領導吩咐:");
    System.out.println("首先,產品經理規劃新需求...");
    executorService.submit(thread1);
    System.out.println("然後,開發人員開發新需求功能...");
    executorService.submit(thread2);
    System.out.println("最後,測試人員測試新功能...");
    executorService.submit(thread3);
    executorService.shutdown();
}

輸出如下:
早上:
產品經理來上班了
測試人員來上班了
開發人員來上班了
領導吩咐:
首先,產品經理規劃新需求...
然後,開發人員開發新需求功能...
最後,測試人員測試新功能...
產品經理規劃新需求
開發人員開發新需求功能
測試人員測試新功能

4、.使用線程的CountDownLatch(倒計數)方法
 

/**
 * 用於判斷線程一是否執行,倒計時設置爲1,執行後減1
 */
private static CountDownLatch c1 = new CountDownLatch(1);

/**
 * 用於判斷線程二是否執行,倒計時設置爲1,執行後減1
 */
private static CountDownLatch c2 = new CountDownLatch(1);

public static void main(String[] args) {
    final Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("產品經理規劃新需求");
            //對c1倒計時-1
            c1.countDown();
        }
    });

    final Thread thread2 = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                //等待c1倒計時,計時爲0則往下運行
                c1.await();
                System.out.println("開發人員開發新需求功能");
                //對c2倒計時-1
                c2.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });

    Thread thread3 = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                //等待c2倒計時,計時爲0則往下運行
                c2.await();
                System.out.println("測試人員測試新功能");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });

    System.out.println("早上:");
    System.out.println("測試人員來上班了...");
    thread3.start();
    System.out.println("產品經理來上班了...");
    thread1.start();
    System.out.println("開發人員來上班了...");
    thread2.start();

輸出結果如下:
早上:
測試人員來上班了...
產品經理來上班了...
開發人員來上班了...
產品經理規劃新需求
開發人員開發新需求功能
測試人員測試新功能

5、使用CyclicBarrier(迴環柵欄)實現線程按順序運行

 

static CyclicBarrier barrier1 = new CyclicBarrier(2);
static CyclicBarrier barrier2 = new CyclicBarrier(2);

public static void main(String[] args) {

    final Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                System.out.println("產品經理規劃新需求");
                //放開柵欄1
                barrier1.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    });

    final Thread thread2 = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                //放開柵欄1
                barrier1.await();
                System.out.println("開發人員開發新需求功能");
                //放開柵欄2
                barrier2.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    });

    final Thread thread3 = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                //放開柵欄2
                barrier2.await();
                System.out.println("測試人員測試新功能");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    });

    System.out.println("早上:");
    System.out.println("測試人員來上班了...");
    thread3.start();
    System.out.println("產品經理來上班了...");
    thread1.start();
    System.out.println("開發人員來上班了...");
    thread2.start();

輸出結果:
早上:
測試人員來上班了...
產品經理來上班了...
開發人員來上班了...
產品經理規劃新需求
開發人員開發新需求功能
測試人員測試新功能

6、使用Sephmore(信號量)實現線程按順序運行
 

Sephmore(信號量):Semaphore是一個計數信號量,從概念上將,Semaphore包含一組許可證,如果有需要的話,每個acquire()方法都會阻塞,直到獲取一個可用的許可證,每個release()方法都會釋放持有許可證的線程,並且歸還Semaphore一個可用的許可證。然而,實際上並沒有真實的許可證對象供線程使用,Semaphore只是對可用的數量進行管理維護。

acquire():當前線程嘗試去阻塞的獲取1個許可證,此過程是阻塞的,當前線程獲取了1個可用的許可證,則會停止等待,繼續執行。

release():當前線程釋放1個可用的許可證。

 

public static Semaphore semaphoreA = new Semaphore(1);
public static Semaphore semaphoreB = new Semaphore(1);
public static Semaphore semaphoreC = new Semaphore(1);

public static void main(String[] args) throws InterruptedException {
    semaphoreB.acquire();//ABC線程啓動之前 獲取SemaphoreB的1個資源,保證線程A最先執行
    semaphoreC.acquire();//ABC線程啓動之前 獲取SemaphoreC的1個資源,保證線程A最先執行
    Thread a=new Thread(new Runnable(){
        @Override
        public void run() {
            try {
                semaphoreA.acquire();
                System.out.print("A");
                semaphoreB.release();//之前說的特性:可以在ThreadA釋放ThreadB的Semaphore資源, 下同
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    Thread b=new Thread(new Runnable(){
        @Override
        public void run() {
            try {
                semaphoreB.acquire();
                System.out.print("B");
                semaphoreC.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    Thread c=new Thread(new Runnable(){
        @Override
        public void run() {
            try {
                semaphoreC.acquire();
                System.out.println("C");
                semaphoreA.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    c.start();
    b.start();
    a.start();
}

直接結果如下:
ABC

本次使用三個信號量來控制三個線程按照順序執行

具體參考:https://www.cnblogs.com/wenjunwei/p/10573289.html  這篇文章中的信號量程序有問題
https://www.jianshu.com/p/bed37328e3b0   使用這篇文章信號量程序問題
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章