多線程-按序打印的4種寫法

題目描述:

我們提供了一個類:

public class Foo {

? public void one() { print("one"); }

? public void two() { print("two"); }

? public void three() { print("three"); }

}

三個不同的線程將會共用一個?Foo?實例。

線程 A 將會調(diào)用 one() 方法

線程 B 將會調(diào)用?two() 方法

線程 C 將會調(diào)用 three() 方法

請設計修改程序,以確保 two() 方法在 one() 方法之后被執(zhí)行,three() 方法在 two() 方法之后被執(zhí)行。

示例 1:

輸入: [1,2,3]

輸出: "onetwothree"

解釋:

有三個線程會被異步啟動。

輸入 [1,2,3] 表示線程 A 將會調(diào)用 one() 方法,線程 B 將會調(diào)用 two() 方法,線程 C 將會調(diào)用 three() 方法。

正確的輸出是 "onetwothree"。

示例 2:

輸入: [1,3,2]

輸出: "onetwothree"

解釋:

輸入 [1,3,2] 表示線程 A 將會調(diào)用 one() 方法,線程 B 將會調(diào)用 three() 方法,線程 C 將會調(diào)用 two() 方法。

正確的輸出是 "onetwothree"。

解法1:利用鎖、成員變量來控制順序。

first方法直接打印one,并設置flag = 1,并喚醒其他所有線程。

second方法 如果flag != 1 那么輪詢,并等待。當first方法執(zhí)行完后,那么flag = 1,此時second會執(zhí)行while塊之后的,即打印two,并設置flag = 2。如first執(zhí)行完后,third方法先執(zhí)行,因此時flag =1,那么third方法會一直在輪詢。

third方法在second執(zhí)行完后,flag = 2,并喚醒其他所有線程,此時處于wait狀態(tài)的只有third,因此,在second執(zhí)行完后即會執(zhí)行third方法。

class Foo {

? ? private? Object lock =? new Object();

? ? private int flag = 0;

? ? volatile int count = 1;

? ? public Foo() {

? ? }

? ? public void first(Runnable printFirst) throws InterruptedException {

? ? ? ? printFirst.run();

? ? ? ? count++;

? ? ? ? // printFirst.run() outputs "first". Do not change or remove this line.

? ? }

? ? public void second(Runnable printSecond) throws InterruptedException {

? ? ? ? while(count != 2);

? ? ? ? printSecond.run();

? ? ? ? count++;

? ? ? ? // printSecond.run() outputs "second". Do not change or remove this line.

? ? }

? ? public void third(Runnable printThird) throws InterruptedException {

? ? ? ? while(count != 3);

? ? ? ? ? printThird.run();

? ? ? ? // printThird.run() outputs "third". Do not change or remove this line.

? ? }

}

解法2:利用volatile變量

class Foo {

? ? volatile int count = 1;

? ? public Foo() {

? ? }

? ? public void first(Runnable printFirst) throws InterruptedException {

? ? ? ? printFirst.run();

? ? ? ? count++;

? ? ? ? // printFirst.run() outputs "first". Do not change or remove this line.

? ? }

? ? public void second(Runnable printSecond) throws InterruptedException {

? ? ? ? while(count != 2);

? ? ? ? printSecond.run();

? ? ? ? count++;

? ? ? ? // printSecond.run() outputs "second". Do not change or remove this line.

? ? }

? ? public void third(Runnable printThird) throws InterruptedException {

? ? ? ? while(count != 3);

? ? ? ? ? printThird.run();

? ? ? ? ? count = 1;

? ? ? ?// printThird.run() outputs "third". Do not change or remove this line.

? ? }

}

解法3:利用CountDownLatch,CountDownLatch是java.util.concurrent包下面的一個工具類,可以用來協(xié)調(diào)多個線程之間的同步,或者說起到線程之間的通信(而不是用作互斥的作用)。 它可以允許一個或者多個線程等待其他線程完成操作。

簡單點說,直到CountDownLatch里面計數(shù)為0才執(zhí)行所有線程。

首先初始化兩個數(shù)量均為1的計數(shù)器

first方法,在執(zhí)行cdla.countDown();之后 cdla 計數(shù)為0。

second方法,cdla.await(); 如果cdla里計數(shù)不為0,那么會一直阻塞在此,直到cdla計數(shù)為0,即first方法執(zhí)行完之后,才會通過。此時再執(zhí)行cdlb.countDown();,cdlb計數(shù)為0。

third方法cdlb.await();如果cdlb里計數(shù)不為0,那么會一直阻塞在此,直到cdlb計數(shù)為0,即second方法執(zhí)行完之后。

class Foo {

? ? private CountDownLatch a;

? ? private CountDownLatch b;

? ? public Foo() {

? ? ? ? a = new CountDownLatch(1);

? ? ? ? b = new CountDownLatch(1);

? ? }

? ? public void first(Runnable printFirst) throws InterruptedException {

? ? ? ? printFirst.run();

? ? ? ? a.countDown();

? ? ? ? // printFirst.run() outputs "first". Do not change or remove this line.

? ? }

? ? public void second(Runnable printSecond) throws InterruptedException {

? ? ? ? a.await();

? ? ? ? printSecond.run();

? ? ? ? b.countDown();

? ? ? ? // printSecond.run() outputs "second". Do not change or remove this line.

? ? }

? ? public void third(Runnable printThird) throws InterruptedException {

? ? ? ? ? b.await();

? ? ? ? ? printThird.run();

? ? ? ? // printThird.run() outputs "third". Do not change or remove this line.

? ? }

}


解法4:利用信號量Semaphore

Semaphore 是 synchronized 的加強版,作用是控制線程的并發(fā)數(shù)量。

如果我們設置Semaphore 里初始值為0,就是一開始使線程阻塞從而完成其他執(zhí)行。

原理和CountDownLatch 差不多。

first方法直接釋放(初始值為0,是可以釋放的)。

second方法在最開始會獲取spa,只有first方法執(zhí)行完之后,才能在此處獲取到,即只有first執(zhí)行完之后才會執(zhí)行second。并釋放spb。

third方法在最開始會獲取spb,spb釋放是在second執(zhí)行完之后,因此只有在second執(zhí)行完之后才會執(zhí)行third。

class Foo {

? ? private Semaphore a;

? ? private Semaphore b;

? ? public Foo() {

? ? ? ? a = new Semaphore(0);

? ? ? ? b = new Semaphore(0);

? ? }

? ? public void first(Runnable printFirst) throws InterruptedException {

? ? ? ? printFirst.run();

? ? ? ? a.release();

? ? ? ? // printFirst.run() outputs "first". Do not change or remove this line.

? ? }

? ? public void second(Runnable printSecond) throws InterruptedException {

? ? ? ? a.acquire();

? ? ? ? printSecond.run();

? ? ? ? b.release();

? ? ? ? // printSecond.run() outputs "second". Do not change or remove this line.

? ? }

? ? public void third(Runnable printThird) throws InterruptedException {

? ? ? ? ? b.acquire();

? ? ? ? ? printThird.run();

? ? ? ? // printThird.run() outputs "third". Do not change or remove this line.

? ? }

}


測試代碼:

public static void main(String[] args) {

Foo foo =new Foo();

? ? for (int i =0; i <8; i++) {

ExecutorService executor = Executors.newFixedThreadPool(3);

? ? ? ? executor.submit(() -> {

try {

foo.first(() -> {

System.out.println("one");

? ? ? ? ? ? ? ? });

? ? ? ? ? ? }catch (InterruptedException e) {

e.printStackTrace();

? ? ? ? ? ? }

});

? ? ? ? executor.submit(() -> {

try {

foo.second(() -> {

System.out.println("two");

? ? ? ? ? ? ? ? });

? ? ? ? ? ? }catch (InterruptedException e) {

e.printStackTrace();

? ? ? ? ? ? }

});

? ? ? ? executor.submit(() -> {

try {

foo.third(() -> {

System.out.println("three");

? ? ? ? ? ? ? ? });

? ? ? ? ? ? }catch (InterruptedException e) {

e.printStackTrace();

? ? ? ? ? ? }

});

? ? ? ? executor.isShutdown();

? ? }

}




個人座右銘:主動? 行動? 思考? ?反省? 總結(jié)


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 本文主要講了java中多線程的使用方法、線程同步、線程數(shù)據(jù)傳遞、線程狀態(tài)及相應的一些線程函數(shù)用法、概述等。 首先講...
    李欣陽閱讀 2,604評論 1 15
  • Java多線程學習 [-] 一擴展javalangThread類 二實現(xiàn)javalangRunnable接口 三T...
    影馳閱讀 3,117評論 1 18
  • JUC 原創(chuàng)者:文思,感謝尚硅谷,資料來源于尚硅谷 目錄: 1、volatile關鍵字與內(nèi)存可見性 2、原子變量與...
    文思li閱讀 2,540評論 0 1
  • 先看幾個概念:線程:進程中負責程序執(zhí)行的執(zhí)行單元。一個進程中至少有一個線程。多線程:解決多任務同時執(zhí)行的需求,合理...
    yeying12321閱讀 614評論 0 0
  • 接下來介紹比synchronized功能上更豐富的關鍵字:重入鎖 靈活性:public class Reentra...
    innoyiya閱讀 383評論 0 1

友情鏈接更多精彩內(nèi)容