題目描述:
我們提供了一個類:
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();
? ? }
}