1. 程式人生 > 程式設計 >[Java多執行緒][LeetCode題解]1114.按序列印

[Java多執行緒][LeetCode題解]1114.按序列印

[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() 方法之後被執行。

題解思路

第一次看到多執行緒的題目,有點意思

其實題意也很簡單,就是能讓多執行緒程式按順序輸出,正好學習了高併發程式設計,上手試試

  1. 模擬一個簡易的產品生產鏈環境,提供一個productionChain棧(或者佇列)當做原料倉(簡易的倉庫裡堆積物品,先堆上去的東西后面找半天才拿的出來hhh),用於存放原料,順便初始化交個房租什麼的
    private
    Stack<Integer> productionChain ; public Foo() { // Init Resources Pool productionChain = new Stack<>(); } 複製程式碼
  2. 然後先算一算需要哪些原料,原料之間存在怎樣的依賴關係
    // 題意需要呼叫3個方法,後兩種分別依賴前兩種
    // 那麼有依賴關係:1 -> 2 -> 3
    // 此處給出兩種原料id,如果有更多的依賴條件就需要考慮更多
    private final int pidOfFirst = 1;
    private final int pidOfSecond = 2
    ; // 同時需要有資源鎖,可以當做一個生產廠房有限的生產器械,只能同時供一種原料生產或加工 private final Object resLock = new Object(); 複製程式碼
  3. 生產線設計
    // 由題目提供的程式碼設定有以下三條生產線,要求按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();
    }
    複製程式碼
  4. 原料分配和產品加工
  • 原料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. 簡單測試生產線,樣例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. 簡單測試生產線,樣例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