[題解] 條件變數實現數字的交替輸出

酒冽發表於2021-12-03

今天在實驗室摸魚逛部落格園,看到一個併發題目,考察條件變數的使用:

使用條件變數(AQS.Condition),實現兩個執行緒交替輸出從0到100,規定執行緒A先輸出,執行緒B後輸出,前者輸出偶數,後者輸出奇數

我的實現方法如下:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class Solution {
    private final static ReentrantLock lock = new ReentrantLock();
    static int value = 0;

    public static void main(String[] args) {

        Condition l0 = lock.newCondition();
        Condition l1 = lock.newCondition();

        System.out.println("Tread A first print!");

        Thread threadA = new Thread(() -> {
            while (true) {
                if (value >= 100) {
                    break;
                }
                try {
                    lock.lock();
                    while(value % 2 != 0) {
                        l0.await();
                    }
                    System.out.println("Thread A start to print —— " + value++);
                    l1.signalAll();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        });

        Thread threadB = new Thread(() -> {
            while (true) {
                if (value >= 100) {
                    break;
                }
                try {
                    lock.lock();
                    while(value % 2 == 0) {
                        l1.await();
                    }
                    System.out.println("Thread B start to print —— " + value++);
                    l0.signalAll();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        });

        threadA.start();
        threadB.start();
    }
}

之前雖然把AQS、ReentrantLock的原始碼都看了好幾遍,但是真正自己動手寫多執行緒程式碼,還是有些生疏

值得注意的有三點:

  • 使用條件變數之前,必須先獲取鎖。如果是ReentrantLock建立出來的條件變數,必須將條件變數的await和signal方法包裹在lock和unlock中使用。await方法會釋放獲取到的鎖,當等待的條件滿足時,會重新獲取鎖,這點不用擔心
  • 如果要同步兩個執行緒,需要兩個條件變數,另外還需要外界的一個條件來幫助判斷,光靠兩個條件變數的await和signal會發生死鎖。這裡使用的條件是value是否為偶數。如果是生產者-消費者模型中,條件就應該是佇列是否為空。這些條件都應該放在while迴圈而不是if條件中,這是為了防止虛假喚醒
  • 以後要多練練使用Idea除錯多執行緒程式碼,不容易但也不難

相關文章