顯示鎖Lock

Lock簡(jiǎn)單介紹

public class LockDemo {

    // 聲明一個(gè)lock鎖
    private Lock lock = new ReentrantLock();

    private int count;

    // 用lock來(lái)加鎖
    public void increament01() {
        // 加鎖
        lock.lock();
        try {
            count++;
        } finally {
            // 釋放鎖
            lock.unlock();
        }
    }

    // syn關(guān)鍵字加鎖
    public synchronized void increament02() {
        count++;
    }

}

釋放鎖的代碼應(yīng)當(dāng)放在finally關(guān)鍵字中,以保證代碼出現(xiàn)異常后,鎖能夠及時(shí)的釋放掉。

syn和lock的比較

  1. syn稱(chēng)之為內(nèi)置鎖,因?yàn)樗且粋€(gè)關(guān)鍵字,在jdk中沒(méi)有源碼,經(jīng)過(guò)多年優(yōu)化,性能很優(yōu)秀,并且代碼較為簡(jiǎn)潔,但較lock有局限性。
  2. lock稱(chēng)之為顯示鎖,在獲取鎖的過(guò)程中可以被中斷,并且還可以超時(shí)獲取鎖,和嘗試加鎖,syn則沒(méi)有這三個(gè)特點(diǎn)。
  3. syn和lock都是可重入鎖。
  4. ReentrantLock和syn關(guān)鍵字都是排他鎖,即在同一個(gè)時(shí)刻只允許一個(gè)線程訪問(wèn)。

總結(jié):如果沒(méi)有第二條中的業(yè)務(wù)場(chǎng)景的時(shí)候,應(yīng)該用syn關(guān)鍵字。

公平鎖和非公平鎖

如果在時(shí)間上,先對(duì)鎖進(jìn)行獲取的請(qǐng)求,一定先被滿足,這個(gè)鎖就是公平的,如果不滿足,這個(gè)鎖就不非公平的,一般來(lái)說(shuō),非公平鎖效率相對(duì)較高。

    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

源碼看出,lock默認(rèn)是非公平的鎖。

Lock和Condition

舉例說(shuō)明:

下面是一個(gè)快遞類(lèi):一個(gè)lock下面可以有多個(gè)condition,因?yàn)樾枰O(jiān)控兩個(gè)條件的變化,所以定義了兩個(gè)condition來(lái)鎖這兩個(gè)變量。lock鎖不會(huì)自動(dòng)釋放,需要手動(dòng)釋放。使線程進(jìn)入等待狀態(tài)的方法不是object的wait方法,而是condition接口中的await方法,喚醒線程的方法也是condition接口中的signal和signalAll,這點(diǎn)很重要。

public class Express {
    // 聲明鎖
    private Lock lock = new ReentrantLock();
    // 公里變化鎖
    private Condition kmCondition = lock.newCondition();
    // 地點(diǎn)變化鎖
    private Condition siteCondition = lock.newCondition();
    // 始發(fā)地
    private final static String CITY = "ShangHai";
    // 里程變化
    private int km;
    // 地點(diǎn)變化
    private String site;

    Express() {

    }

    Express(int km, String site) {
        this.km = km;
        this.site = site;
    }

    // 里程數(shù)變化,會(huì)喚起線程
    public void changeKm() {
        lock.lock();
        try {
            this.km = 101;
            // 喚醒
            kmCondition.signal();
        } finally {
            lock.unlock();
        }
    }

    // 地點(diǎn)變化會(huì)喚起線程
    public void changeSite() {
        lock.lock();
        try {
            this.site = "BeiJing";
            // 喚醒
            siteCondition.signal();
        } finally {
            lock.unlock();
        }
    }

    // 用來(lái)監(jiān)聽(tīng)里程數(shù)的變化
    public void waitKm() {
        lock.lock();
        try {
            while (this.km <= 100) {
                System.out.println(Thread.currentThread().getId() + "監(jiān)控【公里數(shù)】變化的線程即將進(jìn)入等待狀態(tài)。。。");
                kmCondition.await();
                System.out.println(Thread.currentThread().getId() + "-號(hào)監(jiān)聽(tīng)===里程變化===的線程被喚醒了。。。");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
            System.out.println(Thread.currentThread().getId() + "釋放了鎖。");
        }
        System.out.println(Thread.currentThread().getId() + "-號(hào)監(jiān)聽(tīng)===里程變化===的線程去做相應(yīng)的事了");
    }

    // 用來(lái)監(jiān)聽(tīng)地點(diǎn)的變化
    public void waitSite() {
        lock.lock();
        try {
            while (CITY.equals(this.site)) {
                System.out.println(Thread.currentThread().getId() + "監(jiān)控【地點(diǎn)】變化的線程即將進(jìn)入等待狀態(tài)。。。");
                siteCondition.await();
                System.out.println(Thread.currentThread().getId() + "-號(hào)監(jiān)聽(tīng)===地點(diǎn)變化===的線程被喚醒了。。。");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
            System.out.println(Thread.currentThread().getId() + "釋放了鎖。");
        }
        System.out.println(Thread.currentThread().getId() + "-號(hào)監(jiān)聽(tīng)===地點(diǎn)變化===的線程去做相應(yīng)的事了");
    }

}

main方法測(cè)試類(lèi):

分別啟動(dòng)三個(gè)監(jiān)控地點(diǎn)變化的線程和三個(gè)監(jiān)控公里數(shù)變化的線程,并使它們都進(jìn)入等待狀態(tài),然后改變地點(diǎn)變化。調(diào)用await方法后,每個(gè)被阻塞的線程都會(huì)加到隊(duì)列末尾排隊(duì)。

public class Test {
    // 初始化快遞
    private static Express express = new Express(0, "ShangHai");
    // 用來(lái)監(jiān)聽(tīng)里程數(shù)變化的線程
    static class CheckKm implements Runnable {
        public void run() {
            express.waitKm();
        }
    }

    // 用來(lái)監(jiān)聽(tīng)地點(diǎn)變化的線程
    static class CheckSite implements Runnable {
        public void run() {
            express.waitSite();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        // 啟動(dòng)三個(gè)線程去監(jiān)聽(tīng)里程數(shù)的變化
        for (int i = 0; i <= 2; i++) {
            new Thread(new CheckKm()).start();
        }

        // 啟動(dòng)三個(gè)線程去監(jiān)聽(tīng)地點(diǎn)的變化
        for (int i = 0; i <= 2; i++) {
            new Thread(new CheckSite()).start();
        }

        // 主線程睡眠一秒,異常信息拋出去
        Thread.sleep(1000);

        // 讓快遞的地點(diǎn)發(fā)生變化
        express.changeSite();
    }
}
  1. 首先使用的是用signal來(lái)喚醒線程:?jiǎn)拘言诖薒ock對(duì)象上等待的單個(gè)線程。
11監(jiān)控【公里數(shù)】變化的線程即將進(jìn)入等待狀態(tài)。。。
13監(jiān)控【公里數(shù)】變化的線程即將進(jìn)入等待狀態(tài)。。。
14監(jiān)控【地點(diǎn)】變化的線程即將進(jìn)入等待狀態(tài)。。。
15監(jiān)控【地點(diǎn)】變化的線程即將進(jìn)入等待狀態(tài)。。。
12監(jiān)控【公里數(shù)】變化的線程即將進(jìn)入等待狀態(tài)。。。
16監(jiān)控【地點(diǎn)】變化的線程即將進(jìn)入等待狀態(tài)。。。
14-號(hào)監(jiān)聽(tīng)===地點(diǎn)變化===的線程被喚醒了。。。
14釋放了鎖。
14-號(hào)監(jiān)聽(tīng)===地點(diǎn)變化===的線程去做相應(yīng)的事了

signal是喚醒當(dāng)前阻塞隊(duì)列中的第一個(gè),而不是隨機(jī)喚醒。

  1. 使用signalAll來(lái)喚醒線程:?jiǎn)拘言诖薒ock對(duì)象上等待的所有線程。
11監(jiān)控【公里數(shù)】變化的線程即將進(jìn)入等待狀態(tài)。。。
15監(jiān)控【地點(diǎn)】變化的線程即將進(jìn)入等待狀態(tài)。。。
13監(jiān)控【公里數(shù)】變化的線程即將進(jìn)入等待狀態(tài)。。。
12監(jiān)控【公里數(shù)】變化的線程即將進(jìn)入等待狀態(tài)。。。
14監(jiān)控【地點(diǎn)】變化的線程即將進(jìn)入等待狀態(tài)。。。
16監(jiān)控【地點(diǎn)】變化的線程即將進(jìn)入等待狀態(tài)。。。
15-號(hào)監(jiān)聽(tīng)===地點(diǎn)變化===的線程被喚醒了。。。
15釋放了鎖。
15-號(hào)監(jiān)聽(tīng)===地點(diǎn)變化===的線程去做相應(yīng)的事了
14-號(hào)監(jiān)聽(tīng)===地點(diǎn)變化===的線程被喚醒了。。。
14釋放了鎖。
14-號(hào)監(jiān)聽(tīng)===地點(diǎn)變化===的線程去做相應(yīng)的事了
16-號(hào)監(jiān)聽(tīng)===地點(diǎn)變化===的線程被喚醒了。。。
16釋放了鎖。
16-號(hào)監(jiān)聽(tīng)===地點(diǎn)變化===的線程去做相應(yīng)的事了

signalAll喚醒所有當(dāng)前隊(duì)列中的等待線程。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容