面試官:三個線程順序執(zhí)行,你來說說有幾種實現(xiàn)方式?
能想起來幾種呢?
先說下要求,就是三個線程,假設是線程 1,2,3, 現(xiàn)在的要求是:必須是線程 1 先執(zhí)行,然后線程 2 再執(zhí)行,最后是線程 3 執(zhí)行,然后有幾種實現(xiàn)方法呢?
其實它的本質就是實現(xiàn),讓線程 2,3 等待線程 1 執(zhí)行完畢,所以重點就是有哪些方法可以讓線程 2,3 等待。
join
第一反應應該就是使用 join 方法,因為 join 本來就是支持這種機制的
比如,我在線程 B 中調用了線程 A 的 join 方法,那么線程 B 就會等線程 A 執(zhí)行結束之后再執(zhí)行
那么具體應該怎么使用嘞?
別慌嘛,我這里有例子,你瞅瞅:
- public class ThreadLoopOne {
- public static void main(String[] args) {
- Thread t1 = new Thread(new Work(null));
- Thread t2 = new Thread(new Work(t1));
- Thread t3 = new Thread(new Work(t2));
- t1.start();
- t2.start();
- t3.start();
- }
- static class Work implements Runnable {
- private Thread beforeThread;
- public Work(Thread beforeThread){
- this.beforeThread = beforeThread;
- }
- @Override
- public void run() {
- // 如果有線程,就 join 進來,沒有的話就直接輸出
- if (beforeThread != null ){
- try {
- beforeThread.join();
- System.out.println("thread start : " + Thread.currentThread().getName());
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }else{
- System.out.println("thread start : " + Thread.currentThread().getName());
- }
- }
- }
- }
CountDownLatch
剛才說了,本質就是讓線程 B,C 等待線程 A 執(zhí)行完畢
那么信號量就是一個不錯的選擇
如果想要實現(xiàn)的話,那大概就是下面這樣:
- public class ThreadLoopTwo {
- public static void main(String[] args) {
- // 設置線程 1 的信號量為 0
- CountDownLatch cOne = new CountDownLatch(0);
- // 設置線程 2 的信號量為 1
- CountDownLatch cTwo = new CountDownLatch(1);
- // 設置線程 3 的信號量為 1
- CountDownLatch cThree = new CountDownLatch(1);
- // 因為 cOne 為 0 ,故 t1 可以直接執(zhí)行
- Thread t1 = new Thread(new Work(cOne,cTwo));
- // 線程 t1 執(zhí)行完畢之后,此時的 cTwo 為 0 , t2 開始執(zhí)行
- Thread t2 = new Thread(new Work(cTwo,cThree));
- // 線程 t2 執(zhí)行完畢,此時 cThree 為 0 , t3 開始執(zhí)行
- Thread t3 = new Thread(new Work(cThree,cThree));
- t1.start();
- t2.start();
- t3.start();
- }
- static class Work implements Runnable{
- CountDownLatch cOne;
- CountDownLatch cTwo;
- public Work(CountDownLatch cOne, CountDownLatch cTwo){
- super();
- this.cOne = cOne;
- this.cTwo = cTwo;
- }
- @Override
- public void run() {
- try {
- // 當前一個線程信號量為 0 時,才執(zhí)行
- cOne.await();
- System.out.println("thread start : " + Thread.currentThread().getName());
- // 后一個線程信號量減 1
- cTwo.countDown();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
使用單個線程池
之所以線程 1,2,3 的執(zhí)行順序無法保證,是因為在編譯器可能會去做一些優(yōu)化,導致沒有辦法按照順序執(zhí)行
如果我們使用單個線程池去執(zhí)行的話,那就沒有這樣的問題了
具體實現(xiàn):
- public class ThreadLoopThree {
- public static void main(String[] args) {
- Thread t1 = new Thread(new Runnable() {
- @Override
- public void run() {
- System.out.println("thread start : " + Thread.currentThread().getName() + " run one");
- }
- });
- Thread t2 = new Thread(new Runnable() {
- @Override
- public void run() {
- System.out.println("thread start : " + Thread.currentThread().getName() + " run two");
- }
- });
- Thread t3 = new Thread(new Runnable() {
- @Override
- public void run() {
- System.out.println("thread start : " + Thread.currentThread().getName() + " run three");
- }
- });
- ExecutorService executor = Executors.newSingleThreadExecutor();
- // 將線程依次加入到線程池中
- executor.submit(t1);
- executor.submit(t2);
- executor.submit(t3);
- // 及時將線程池關閉
- executor.shutdown();
- }
- }
CompletableFuture
如果使用 CompletableFuture 來實現(xiàn)的話,代碼就非常簡潔了
- public class ThreadLoopFour {
- public static void main(String[] args) {
- Thread t1 = new Thread(new Work());
- Thread t2 = new Thread(new Work());
- Thread t3 = new Thread(new Work());
- CompletableFuture.runAsync(()-> t1.start())
- .thenRun(()->t2.start())
- .thenRun(()->t3.start());
- }
- static class Work implements Runnable{
- @Override
- public void run() {
- System.out.println("thread start : " + Thread.currentThread().getName());
- }
- }
- }