題目描述:
我們提供了一個(gè)類:
public class Foo {
? public void one() { print("one"); }
? public void two() { print("two"); }
? public void three() { print("three"); }
}
三個(gè)不同的線程將會(huì)共用一個(gè)?Foo?實(shí)例。
線程 A 將會(huì)調(diào)用 one() 方法
線程 B 將會(huì)調(diào)用?two() 方法
線程 C 將會(huì)調(diào)用 three() 方法
請(qǐng)?jiān)O(shè)計(jì)修改程序颓芭,以確保 two() 方法在 one() 方法之后被執(zhí)行醋粟,three() 方法在 two() 方法之后被執(zhí)行绑雄。
示例 1:
輸入: [1,2,3]
輸出: "onetwothree"
解釋:
有三個(gè)線程會(huì)被異步啟動(dòng)梆靖。
輸入 [1,2,3] 表示線程 A 將會(huì)調(diào)用 one() 方法捏雌,線程 B 將會(huì)調(diào)用 two() 方法盛险,線程 C 將會(huì)調(diào)用 three() 方法妓柜。
正確的輸出是 "onetwothree"拌倍。
示例 2:
輸入: [1,3,2]
輸出: "onetwothree"
解釋:
輸入 [1,3,2] 表示線程 A 將會(huì)調(diào)用 one() 方法赂鲤,線程 B 將會(huì)調(diào)用 three() 方法,線程 C 將會(huì)調(diào)用 two() 方法柱恤。
正確的輸出是 "onetwothree"数初。
解法1:利用鎖、成員變量來(lái)控制順序梗顺。
first方法直接打印one泡孩,并設(shè)置flag = 1,并喚醒其他所有線程寺谤。
second方法 如果flag != 1 那么輪詢仑鸥,并等待。當(dāng)first方法執(zhí)行完后变屁,那么flag = 1眼俊,此時(shí)second會(huì)執(zhí)行while塊之后的,即打印two敞贡,并設(shè)置flag = 2泵琳。如first執(zhí)行完后,third方法先執(zhí)行誊役,因此時(shí)flag =1,那么third方法會(huì)一直在輪詢获列。
third方法在second執(zhí)行完后,flag = 2蛔垢,并喚醒其他所有線程击孩,此時(shí)處于wait狀態(tài)的只有third,因此,在second執(zhí)行完后即會(huì)執(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包下面的一個(gè)工具類创泄,可以用來(lái)協(xié)調(diào)多個(gè)線程之間的同步,或者說起到線程之間的通信(而不是用作互斥的作用)括蝠。 它可以允許一個(gè)或者多個(gè)線程等待其他線程完成操作鞠抑。
簡(jiǎn)單點(diǎn)說,直到CountDownLatch里面計(jì)數(shù)為0才執(zhí)行所有線程忌警。
首先初始化兩個(gè)數(shù)量均為1的計(jì)數(shù)器
first方法搁拙,在執(zhí)行cdla.countDown();之后 cdla 計(jì)數(shù)為0。
second方法法绵,cdla.await(); 如果cdla里計(jì)數(shù)不為0箕速,那么會(huì)一直阻塞在此,直到cdla計(jì)數(shù)為0朋譬,即first方法執(zhí)行完之后盐茎,才會(huì)通過。此時(shí)再執(zhí)行cdlb.countDown();徙赢,cdlb計(jì)數(shù)為0字柠。
third方法cdlb.await();如果cdlb里計(jì)數(shù)不為0,那么會(huì)一直阻塞在此犀忱,直到cdlb計(jì)數(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:利用信號(hào)量Semaphore
Semaphore 是 synchronized 的加強(qiáng)版,作用是控制線程的并發(fā)數(shù)量阴汇。
如果我們?cè)O(shè)置Semaphore 里初始值為0数冬,就是一開始使線程阻塞從而完成其他執(zhí)行。
原理和CountDownLatch 差不多搀庶。
first方法直接釋放(初始值為0拐纱,是可以釋放的)。
second方法在最開始會(huì)獲取spa哥倔,只有first方法執(zhí)行完之后秸架,才能在此處獲取到,即只有first執(zhí)行完之后才會(huì)執(zhí)行second咆蒿。并釋放spb东抹。
third方法在最開始會(huì)獲取spb,spb釋放是在second執(zhí)行完之后沃测,因此只有在second執(zhí)行完之后才會(huì)執(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.
? ? }
}
測(cè)試代碼:
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();
? ? }
}