如何使用ReentrantLock的條件變數,讓多個執行緒順序執行?

千鋒IT教育發表於2022-11-08

如何使用ReentrantLock的條件變數,讓多個執行緒順序執行?

一. 前言

近日有學生在參加某公司校招面試時,遇到一個多個執行緒順序執行的面試題,特意記錄下來和大家分享一下,這個題目的具體要求是這樣的:

假設有3個執行緒 a,b,c,要求三個執行緒一起進入到就緒態,執行時一定要按照 a-->b-->c的順序執行。即使a或者b執行緒進入到了阻塞態,也一定會按照a-->b-->c的順序執行執行緒。請問該如何保證實現這個需求呢?

二. 解決方案

關於這道題,網上常見的實現思路,大致有4種解決方案:

  1. 透過join()方法使當前執行緒“阻塞”,等待指定執行緒執行完畢後繼續執行;
  2. 透過倒數計時器CountDownLatch實現;
  3. 透過建立單一化執行緒池 newSingleThreadExecutor()實現;
  4. 透過ReentrantLock 中的條件變數實現;


今天先使用ReentrantLock 的條件變數來實現這個題目中的需求。

三. 使用ReentrantLock 條件變數

首先我們們來了解一下,什麼是ReentrantLock 條件變數(Condition)。

ReentrantLock 中的條件變數功能,類似於普通 synchronized 的 wait、notify,我們可以使用Reentrantlock 鎖,配合 Condition 物件上的 await()和 signal()或 signalAll()方法,來實現執行緒間協作。與synchronized的wait和notify不同之處在於,ReentrantLock中的條件變數可以有多個,可以實現更精細的控制執行緒。

Condition中常用的方法API有如下這些:

ReentrantLock程式碼實現:

class ShareDataLock{
    // 執行緒執行的條件 1:執行緒1執行 2:執行緒2執行 3:執行緒3執行
    int number =1;
    // 鎖
    Lock lock = new ReentrantLock();
    // 從鎖中獲得3個條件變數
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    Condition condition3 = lock.newCondition();
    // 第一個執行緒run之後執行的方法
    public void f1(){
        lock.lock();
        try {
            // 如果條件值不為1 就掛起等待
            while(number!=1){
                condition1.await();
            }
            // 故意阻塞100毫秒,看看其他的執行緒會不會不再排隊
            Thread.sleep(100);
            System.out.println("------1--------");
            // 執行緒1 執行完畢 把變數設定為2
            number = 2;
            // 喚醒第2個條件變數
            condition2.signal();
        } catch (Exception e) {
          e.printStackTrace();
        } finally {
            // 不管拋沒丟擲異常都要解鎖,防止執行緒死鎖
          lock.unlock();
        }
    }
    
    public void f2(){
        lock.lock();
        try {
            while(number!=2){
                condition2.await();
            }
            System.out.println("------2--------");
            number = 3;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    
    public void f3(){
        lock.lock();
        try {
            while(number!=3){
                condition3.await();
            }
            System.out.println("------3--------");
            number = 1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
public class SynchronizedAndReentrantLockDemo {
    public static void main(String[] args) {
        ShareDataLock shareDataLock = new ShareDataLock();
        for (int i = 0; i < 10; i++) {
            // 3個執行緒分別執行1,2,3 3個方法 ,並且同時就緒
            new Thread(()->shareDataLock.f1(),"AA").start();
            new Thread(()->shareDataLock.f2(),"bb").start();
            new Thread(()->shareDataLock.f3(),"cc").start();
        }
    }
}

程式碼執行效果如下圖:

現在我們就會發現,3個執行緒已經可以被隨意控制了,你會了嗎?

四. 後話

如上文所述,讓多個執行緒按順序執行,網上常見的解決方案有4種。但大家要注意的是,面試官出這個題有一個先決條件,“ 要讓所有的執行緒同時就緒”,所以我們就可以排除使用join方法和使用單一化執行緒池的方案了。那麼要想實現這個面試題中的需求,比較靠譜的方法 只剩下ReentrantLock 中的條件變數和使用倒數計時器CountDownLatch兩種方案了。

今天我們們暫時先介紹條件變數的方法,日後的文章中會介紹怎樣使用CountDownLatch,讓多個執行緒有序執行,敬請各位粉絲們繼續期待哦。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70023145/viewspace-2922446/,如需轉載,請註明出處,否則將追究法律責任。

相關文章