多執行緒中使用Lock鎖定多個條件Condition的使用

select957發表於2020-10-24

Lock

公平鎖

所謂公平鎖就是按照申請鎖的順序來執行,就像排隊取款一樣,前面的人取款結束後面的人才可以取,就是一個佇列。

非公平鎖

非公平鎖是指多個執行緒獲取鎖的順序並不是按照申請鎖的順序,與上面相反有可能後申請鎖的反而先獲得了鎖導致優先順序翻轉;也會出現某個執行緒一直拿不到鎖導致執行緒飢餓現象。
非公平鎖的優點在於吞吐量比公平鎖大,synchronized就是一種非公平鎖

鎖的使用ReentrantLock

ReentrantLock實現了Lock介面,使用時他有無參構造方法和有參構造方法。

無參構造放從原始碼可以看出是呼叫的NonfairSync()方法,這是一個非公平鎖從名字就可以看出。

/**
 * Creates an instance of {@code ReentrantLock}.
 * This is equivalent to using {@code ReentrantLock(false)}.
 */
public ReentrantLock() {
    sync = new NonfairSync();
}

有參構造方法是接收一個boolean型別變數。從程式碼可以看出傳true時例項化的是公平鎖,傳false時例項化的是非公平鎖。

/**
 * Creates an instance of {@code ReentrantLock} with the
 * given fairness policy.
 *
 * @param fair {@code true} if this lock should use a fair ordering policy
 */
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

Condition

Condition裡的await()signal()signalAll()Object監視器方法裡的( wait()notify()notifyAll() )類似。Lock可替換synchronized方法和語句的使用, Condition取代了物件監視器方法的使用。

Condition的常用方法

方法描述
await()使當前執行緒進入掛起等待狀態,等待訊號繼續進行。
signal()喚醒一個等待執行緒。
signalAll()喚醒所有等待執行緒。

Lock繫結多個Condition使用

這裡做一個鏈式喚醒的操作,流程圖如下
在這裡插入圖片描述
鎖的三部曲: 判斷 幹活 喚醒
裡面的判斷為了避免虛假喚醒,一定要採用 while

案例: 做個公司招人面試的流程

  1. 來個一個求職者hr先面試問幾個問題
  2. manager再面試問幾個問題
  3. boss面試問幾個問題,沒有通過告知hr繼續招人

此時三個面試官分別是三個執行緒,按照順序來。模擬面試6個求求職者。(不要糾結為啥都沒通過,只是演示)

建立一個標識

// 標誌位 0:hr 1:manager 2:boss
private int flag = 0;

建立一把重入鎖

// 建立一個重入鎖
private Lock lock = new ReentrantLock();

給這把鎖建立三把鑰匙

// 這三個相當於備用鑰匙
Condition c1 = lock.newCondition();
Condition c2 = lock.newCondition();
Condition c3 = lock.newCondition();
  1. 判斷 判斷標識位是否成立否則就進入掛起等待狀態
  2. 幹活 面試官提問問題
  3. 通知 前一個面試官面試完通知下一個面試官

求職業務類完整程式碼Interview

package com.avatar.juc.sample1;

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

/**
 * 面試需要的流程
 * 1、hr 提問三個問題
 * 2、manager 提問五個問題
 * 3、boss 提問兩個問題
 * 微信公眾號:快樂學習與分享
 */
public class Interview {

    // 標誌位 0:hr 1:manager 2:boss
    private int flag = 0;

    // 建立一個重入鎖
    private Lock lock = new ReentrantLock();

    // 這三個相當於備用鑰匙
    Condition c1 = lock.newCondition();
    Condition c2 =  lock.newCondition();
    Condition c3 =  lock.newCondition();

    /**
     * hr 面試
     * @param num 面試者序號
     */
    public void hrAsk3(int num){
        lock.lock();
        try {
            // 1、判斷flag不等於0是掛起
            while (flag != 0){ // 為了避免虛假喚醒,一定要採用 while
                c1.await();
            }
            System.out.println(Thread.currentThread().getName() + "\t面試第:" + num + "位面試者");
            // 2、工作 hr提問
            for (int i = 1; i <= 3; i++) {
                System.out.println(Thread.currentThread().getName() + "\t提問第:" + i + "個問題");
            }
            // 3、通知 進入下個流程
            flag = 1;
            c2.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    /**
     * manger 面試
     * @param num 面試者序號
     */
    public void mangerAsk5(int num){
        lock.lock();
        try {
            // 1、判斷flag不等於1是掛起
            while (flag != 1){ // 為了避免虛假喚醒,一定要採用 while
                c2.await();
            }
            System.out.println(Thread.currentThread().getName() + "\t面試第:" + num + "位面試者");
            // 2、工作 manger 提問
            for (int i = 1; i <= 5; i++) {
                System.out.println(Thread.currentThread().getName() + "\t提問第:" + i + "個問題");
            }
            // 3、通知 進入下個流程
            flag = 2;
            c3.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    /**
     * boss 面試
     * @param num 面試者序號
     */
    public void bossAsk3(int num){
        lock.lock();
        try {
            // 1、判斷flag不等於2是掛起
            while (flag != 2){ // 為了避免虛假喚醒,一定要採用 while
                c3.await();
            }
            System.out.println(Thread.currentThread().getName() + "\t面試第:" + num + "位面試者");
            // 2、工作 hr提問
            for (int i = 1; i <= 2; i++) {
                System.out.println(Thread.currentThread().getName() + "\t提問第:" + i + "個問題");
            }
            System.out.println("很可惜,沒通過。。。");
            System.out.println("");
            // 3、通知 進入下個流程
            flag = 0;
            c1.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

}

模擬6個人面試測試類完整程式碼LockAndConditionDemo

package com.avatar.juc.sample1;

/**
 * 微信公眾號:快樂學習與分享
 *
 * Lock鎖繫結多個條件condition
 *
 * 案例:面試者面試流程為例,一個面試者來到
 *      hr 先面試 提問3個問題 -> manager 再面試提問5個問題 -> boss最後面試提問2個問題
 *      hr 先面試 提問3個問題 -> manager 再面試提問5個問題 -> boss最後面試提問2個問題
 *      hr 先面試 提問3個問題 -> manager 再面試提問5個問題 -> boss最後面試提問2個問題
 *      。。。。。。
 *
 *      假如共面試6個人(案例只為演示)
 */
public class LockAndConditionDemo {

    public static void main(String[] args) {

        Interview interview = new Interview();

        new Thread(()->{
            for (int i = 1; i <= 6; i++) {
                interview.hrAsk3(i);
            }
        },"hr").start();

        new Thread(()->{
            for (int i = 1; i <= 6; i++) {
                interview.mangerAsk5(i);
            }
        },"manager").start();

        new Thread(()->{
            for (int i = 1; i <= 6; i++) {
                interview.bossAsk3(i);
            }
        },"boss").start();

    }

}

測試列印結果

hr	面試第:1位面試者
hr	提問第:1個問題
hr	提問第:2個問題
hr	提問第:3個問題
manager	面試第:1位面試者
manager	提問第:1個問題
manager	提問第:2個問題
manager	提問第:3個問題
manager	提問第:4個問題
manager	提問第:5個問題
boss	面試第:1位面試者
boss	提問第:1個問題
boss	提問第:2個問題
沒通過。。。

hr	面試第:2位面試者
hr	提問第:1個問題
hr	提問第:2個問題
hr	提問第:3個問題
manager	面試第:2位面試者
manager	提問第:1個問題
manager	提問第:2個問題
manager	提問第:3個問題
manager	提問第:4個問題
manager	提問第:5個問題
boss	面試第:2位面試者
boss	提問第:1個問題
boss	提問第:2個問題
沒通過。。。

......

瞭解更多:
在這裡插入圖片描述

相關文章