[Java多執行緒][LeetCode題解]1114.按序列印
阿新 • • 發佈:2019-12-31
[Java多執行緒][LeetCode題解]
LeetCode題解
1114. 按序列印
來源:力扣(LeetCode) 連結:leetcode-cn.com/problems/pr… 著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。
題目
我們提供了一個類:
public class Foo {
public void one() { print("one"); }
public void two() { print("two"); }
public void three() { print("three"); }
}
複製程式碼
三個不同的執行緒將會共用一個Foo
- 執行緒 A 將會呼叫 one() 方法
- 執行緒 B 將會呼叫 two() 方法
- 執行緒 C 將會呼叫 three() 方法
- 請設計修改程式,以確保 two() 方法在 one() 方法之後被執行,three() 方法在 two() 方法之後被執行。
題解思路
第一次看到多執行緒的題目,有點意思
其實題意也很簡單,就是能讓多執行緒程式按順序輸出,正好學習了高併發程式設計,上手試試
- 模擬一個簡易的產品生產鏈環境,提供一個productionChain棧(或者佇列)當做原料倉(簡易的倉庫裡堆積物品,先堆上去的東西后面找半天才拿的出來hhh),用於存放原料,順便初始化交個房租什麼的
private
- 然後先算一算需要哪些原料,原料之間存在怎樣的依賴關係
// 題意需要呼叫3個方法,後兩種分別依賴前兩種 // 那麼有依賴關係:1 -> 2 -> 3 // 此處給出兩種原料id,如果有更多的依賴條件就需要考慮更多 private final int pidOfFirst = 1; private final int pidOfSecond = 2
- 生產線設計
// 由題目提供的程式碼設定有以下三條生產線,要求按first-second-third的順序進行生產 public void first(Runnable printFirst) throws InterruptedException { // printFirst.run() outputs "first". Do not change or remove this line. printFirst.run(); } public void second(Runnable printSecond) throws InterruptedException { // printSecond.run() outputs "second". Do not change or remove this line. printSecond.run(); } public void third(Runnable printThird) throws InterruptedException { // printThird.run() outputs "third". Do not change or remove this line. printThird.run(); } 複製程式碼
- 原料分配和產品加工
- 原料1生產
public void first(Runnable printFirst) throws InterruptedException { // 交付給生產產商進行生產,佔用工具resLock其他人暫時不得使用 synchronized (resLock){ // 生產 原料1 productionChain.push(pidOfFirst); // printFirst.run() outputs "first". Do not change or remove this line. printFirst.run(); // 生產完成,通知其他工序開始工作 resLock.notifyAll(); } } 複製程式碼
- 原料2生產
public void second(Runnable printSecond) throws InterruptedException { // 交付給生產產商進行生產,佔用工具resLock其他人暫時不得使用 synchronized (resLock){ // 需要先了解資源池是否有需要的原料 while( productionChain.empty() || productionChain.peek() != pidOfFirst ){ // 沒有原料的話拿到工具也沒用,還是坐等原料1吧 resLock.wait(); } // 確認原料1已提供,從資源池取出原料1 productionChain.pop(); // 加工生產原料2 productionChain.push(pidOfSecond); // printSecond.run() outputs "second". Do not change or remove this line. printSecond.run(); // 加工完成,通知其他工序開始工作 resLock.notifyAll(); } } 複製程式碼
- *題目無關,僅作思考——以此類推可以有:原料x生產
public void xth(Runnable printSecond) throws InterruptedException { // 交付給生產產商進行生產,佔用工具resLock其他人暫時不得使用 synchronized (resLock){ // 需要先了解資源池是否有需要的原料 while( productionChain.empty() || productionChain.peek() != pidOfX_d1_th ){ // pidOfX_d1_th:X decrease1 th -> (x-1)th -> 第(x-1)種原料 // 沒有原料的話拿到工具也沒用,還是坐等原料x吧 resLock.wait(); } // 確認原料(x-1)已提供,從資源池取出原料(x-1) productionChain.pop(); // 加工生產原料x productionChain.push(pidOfSecond); // printSecond.run() outputs "second". Do not change or remove this line. printSecond.run(); // 加工完成,通知其他工序開始工作 resLock.notifyAll(); } } // 甚至感覺像數學歸納法…… 複製程式碼
- 原料齊全,組裝成品
public void third(Runnable printThird) throws InterruptedException { synchronized (resLock){ // 由於這裡要求是1->2->3,且1和2也要保持順序,所以只需要判斷是否有原料2即可 while( productionChain.empty() || productionChain.peek() != pidOfSecond ){ // wait for resource resLock.wait(); } // 通過前序工藝加工好的原料進行組裝成品 productionChain.pop(); // 當然,這裡可以改成輸出成品等其他操作,但是至少要消耗掉原料2才能繼續進行下面的操作 // printThird.run() outputs "third". Do not change or remove this line. printThird.run(); } } 複製程式碼
- 簡單測試生產線,樣例1,順序呼叫
public static void main(String[] args) throws InterruptedException { Runnable a = () -> System.out.println("one"); Runnable b = () -> System.out.println("two"); Runnable c = () -> System.out.println("three"); ProductionLine pl = new ProductionLine(); pl.first( a ); pl.second( b ); pl.third( c ); // one // two // three } 複製程式碼
- 簡單測試生產線,樣例2,亂序呼叫
public static void main(String[] args) throws InterruptedException { Runnable a = () -> System.out.println("one"); Runnable b = () -> System.out.println("two"); Runnable c = () -> System.out.println("three"); ProductionLine pl = new ProductionLine(); pl.first( a ); pl.second( c ); //先跑c pl.third( b ); //再跑b // 看看是否還是順序輸出 // one // two // three // OK,沒問題 } 複製程式碼
題解
class Foo {
// 題意需要呼叫3個方法,後兩種分別依賴前兩種
// 那麼有依賴關係:1 -> 2 -> 3
// 此處給出兩種原料id,如果有更多的依賴條件就需要考慮更多
private final int pidOfFirst = 1;
private final int pidOfSecond = 2;
// 同時需要有資源鎖,可以當做一個生產廠房有限的生產器械,只能同時供一種原料生產或加工
private final Object resLock = new Object();
private Stack<Integer> productionChain ;
public Foo() {
// Init Resources Pool
productionChain = new Stack<>();
}
public void first(Runnable printFirst) throws InterruptedException {
// 交付給生產產商進行生產,佔用工具resLock其他人暫時不得使用
synchronized (resLock){
// 生產 原料1
productionChain.push(pidOfFirst);
// printFirst.run() outputs "first". Do not change or remove this line.
printFirst.run();
// 生產完成,通知其他工序開始工作
resLock.notifyAll();
}
}
public void second(Runnable printSecond) throws InterruptedException {
// 交付給生產產商進行生產,佔用工具resLock其他人暫時不得使用
synchronized (resLock){
// 需要先了解資源池是否有需要的原料
while( productionChain.empty() || productionChain.peek() != pidOfFirst ){
// 沒有原料的話拿到工具也沒用,還是坐等原料1吧
resLock.wait();
}
// 確認原料1已提供,從資源池取出原料1
productionChain.pop();
// 加工生產原料2
productionChain.push(pidOfSecond);
// printSecond.run() outputs "second". Do not change or remove this line.
printSecond.run();
// 加工完成,通知其他工序開始工作
resLock.notifyAll();
}
}
public void third(Runnable printThird) throws InterruptedException {
synchronized (resLock){
// 由於這裡要求是1->2->3,且1和2也要保持順序,所以只需要判斷是否有原料2即可
while( productionChain.empty() || productionChain.peek() != pidOfSecond ){
// wait for resource
resLock.wait();
}
// 通過前序工藝加工好的原料進行組裝成品
productionChain.pop();
// 當然,這裡可以改成輸出成品等其他操作,但是至少要消耗掉原料2才能繼續進行下面的操作
// printThird.run() outputs "third". Do not change or remove this line.
printThird.run();
}
}
}
複製程式碼
後記
順便回憶了一下執行緒建立、通訊和鎖的使用,以聯絡實際的場景來刷題目還是挺有趣的:D