Lock接口之Condition接口

以前在寫顯示鎖的是後,在顯示鎖的接口中,提到了new Condition這個方法,這個方法會返回一個Condition對象html

簡單介紹一下java

Condition接口:

  任意一個Java對象,都擁有一組監視器方法(定義在java.lang.Object上),主要包括wait()、wait(long timeout)、notify()以及notifyAll()方法,這些方法與synchronized同步關鍵字配合,能夠實現等待/通知模式。以前寫過一篇線程之間的協做(等待通知模式)是使用Object的wait和notify/notifyAll+Synchronized寫的數據庫

  換而言之,synchronized關鍵字想要實現等待/通知模式,須要調用以上的四種方法。express

  而後咱們的Condition接口也提供了可以實現等待/通知模式,是與Lock配合實現的。編程

  我感受這個Condition和那個差很少,也是用來完成線程之間的協做的網絡

  可是兩者在使用方式上以及功能特性上仍是有所差異的。併發

Object對比Condition:

 

 由此表能夠看出,condition接口能夠有多個等待隊列,而object監視器方法只有一個隊列,並且還不支持在等待狀態響應中斷,還不支持當前線程釋放鎖並進入等待狀態到未來的某個時間。ide

示例:

  也不打算寫新的示例了,用這個Condition接口改造一下以前使用等待通知模式的那個案例吧post

  Condition定義了等待/通知兩種類型的方法,當前線程調用這些方法時,須要提早獲取到Condition對象關聯的鎖。Condition對象是由Lock對象(調用Lock對象的newCondition()方法)建立出來的。其實就是,Condition是依賴Lock對象的。就像使用wait/notify須要依賴Synchronized鎖同樣,Condition的使用方式比較簡單,須要注意在調用方法前獲取鎖測試

建立等待通知類

package org.dance.day4.condition;

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

/**
 * 類說明:使用Condition接口實現等待通知模式
 */
public class ExpressCond {

    public final static String CITY = "ShangHai";

    /**
     * 快遞運輸里程數
     */
    private int km;

    /**
     * 快遞到達地點
     */
    private String site;

    /**
     * 建立顯示鎖
     */
    private final Lock lock = new ReentrantLock();

    /**
     * 檢測城市變化
     */
    private final Condition siteCond = lock.newCondition();

    /**
     * 檢測千米數變化
     */
    private final Condition kmCond = lock.newCondition();

    public ExpressCond() {
    }

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

    /* 變化千米數,而後通知處於wait狀態並須要處理千米數的線程進行業務處理*/
    public void changeKm() {
        // 獲取鎖
        lock.lock();
        try {
            this.km = 101;
            // 喚醒在kmCond 上 等待的線程
            kmCond.signal();
        } finally {
            lock.unlock();
        }
    }

    /* 變化地點,而後通知處於wait狀態並須要處理地點的線程進行業務處理*/
    public void changeSite() {
        // 獲取鎖
        lock.lock();
        try {
            this.site = "BeiJing";
            // 喚醒在siteCond 上 等待的線程
            siteCond.signal();
        } finally {
            lock.unlock();
        }
    }

    /*當快遞的里程數大於100時更新數據庫*/
    public void waitKm() {
        lock.lock();
        try {
            while (this.km <= 100) {
                try {
                    kmCond.await();
                } catch (InterruptedException e) {
                    // 處理線程中斷
                    Thread.currentThread().interrupt();
                    e.printStackTrace();
                }
                System.out.println("check km thread[" + Thread.currentThread().getId()
                        + "] is be notifed.");
            }
        } finally {
            lock.unlock();
        }
        System.out.println("the Km is " + this.km + ",I will change db");
    }

    /*當快遞到達目的地時通知用戶*/
    public void waitSite() {
        lock.lock();
        try {
            while (CITY.equals(this.site)) {
                try {
                    siteCond.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("check site thread[" + Thread.currentThread().getId()
                        + "] is be notifed.");
            }
        } finally {
            lock.unlock();
        }
        System.out.println("the site is " + this.site + ",I will call user");
    }
}

經過代碼能夠看見,咱們一個鎖,是能夠攜帶多個等待隊列的

建立測試類

package org.dance.day4.condition;

/**
 *類說明:測試Lock和Condition實現等待通知
 */
public class TestCond {
    private static ExpressCond express = new ExpressCond(0,ExpressCond.CITY);

    /*檢查里程數變化的線程,不知足條件,線程一直等待*/
    private static class CheckKm extends Thread{
        @Override
        public void run() {
            express.waitKm();
        }
    }

    /*檢查地點變化的線程,不知足條件,線程一直等待*/
    private static class CheckSite extends Thread{
        @Override
        public void run() {
            express.waitSite();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for(int i=0;i<3;i++){
            new CheckSite().start();
        }
        for(int i=0;i<3;i++){
            new CheckKm().start();
        }

        Thread.sleep(1000);
        express.changeKm();//快遞里程變化
    }
}

執行結果:

check km thread[14] is be notifed.
the Km is 101,I will change db

經過執行結果,咱們能夠清晰的看到,他是直接喚醒了,在千米數變化上等待的線程的,在以前的等待通知模式中,也就是wait/notify/notifyAll+Sync實現的等待通知模式中,推薦你們使用notifyAll()來喚醒正在等待中的線程,可是在使用Condition接口中,推薦你們使用signal,而不是signalAll().爲啥呢?由於wait/notify/notifyAll是Object的方法,在指定的對象中等待的多是多個線程,分別在檢測不一樣的變量,可能形成信號的攔截,因此推薦使用所有喚醒,,可是在使用Condition上卻不是,由於他是多個等待隊列,他清晰的知道本身應該喚醒那個線程,因此推薦使用signal,至於Condition的實現分析暫時先不寫,等寫完AQS再寫方便,你們理解,我感受不是全部相關的知識都要堆在一塊兒,要是理解不了,再深刻也沒用

做者:彼岸舞

時間:2020\11\04

內容關於:併發編程

本文來源於網絡,只作技術分享,一律不負任何責任

相關文章
相關標籤/搜索