LeetCode-1115.交替打印FooBar(多線程)

LeetCode 題目描述

我們提供一個類:

class FooBar {
  public void foo() {
    for (int i = 0; i < n; i++) {
      print("foo");
    }
  }

  public void bar() {
    for (int i = 0; i < n; i++) {
      print("bar");
    }
  }
}

兩個不同的線程將會共用一個 FooBar 實例。其中一個線程將會調用 foo() 方法,另一個線程將會調用 bar() 方法。

請設計修改程序,以確保 “foobar” 被輸出 n 次。

示例 1:

輸入: n = 1
輸出: "foobar"
解釋: 這裏有兩個線程被異步啓動。其中一個調用 foo() 方法, 另一個調用 bar() 方法,"foobar" 將被輸出一次。

示例 2:

輸入: n = 2
輸出: "foobarfoobar"
解釋: "foobar" 將被輸出兩次。

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/print-foobar-alternately
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

題解

解題思路爲:

  • 多線程併發訪問同一個對象的成員方法
  • 成員方法的執行順序是固定的(突破點)

對於多線程固定順序的執行,我們可以採用 Thread.join 方法。但此處不適用,此處我們只可以修改方法內部實現機制,並不能控制線程執行的方式,所以應該是線程等待喚醒機制。

順序執行實現方案:

  • 方法內部使用布爾變量控制當前方法是否可以執行,即 if(boolean)` 範式,某個方法執行後修改布爾值達到順序控制
  • 使用併發工具類 Semaphore 實現(初始許可爲 0,執行前置條件後釋放一個許可)(感興趣可嘗試實現)

此處如果使用 CyclicBarrier 工具類不能保證 bar 在 foo 前執行,需要額外控制。

方法內部使用布爾變量控制實現

對於 synchronized 關鍵字,該實現添加在方法簽名上而不是 for 循環內部,減少鎖的操作,而是在獲取鎖後使用等待通知機制喚醒後繼續執行。可參考博文-Java 線程等待通知機制(wait、notify)

class FooBar {
    private volatile boolean tmp = false;
    private int n;

    public FooBar(int n) {
        this.n = n;
    }

    public synchronized void foo(Runnable printFoo) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            if (tmp) {
                wait();
            }
            // printFoo.run() outputs "foo". Do not change or remove this line.
            printFoo.run();
            tmp = true;
            notify();
        }
    }

    public synchronized void bar(Runnable printBar) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            if (!tmp) {
                wait();
            }
            // printBar.run() outputs "bar". Do not change or remove this line.
            printBar.run();
            tmp = false;
            notify();
        }
    }
}

參考

有關併發編程相關知識參考本博客專欄對應文章。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章