之前在寫顯示鎖的是後,在顯示鎖的介面中,提到了new Condition這個方法,這個方法會返回一個Condition物件
簡單介紹一下
Condition介面:
任意一個Java物件,都擁有一組監視器方法(定義在java.lang.Object上),主要包括wait()、wait(long timeout)、notify()以及notifyAll()方法,這些方法與synchronized同步關鍵字配合,可以實現等待/通知模式。之前寫過一篇執行緒之間的協作(等待通知模式)是使用Object的wait和notify/notifyAll+Synchronized寫的
換而言之,synchronized關鍵字想要實現等待/通知模式,需要呼叫以上的四種方法。
然後我們的Condition介面也提供了能夠實現等待/通知模式,是與Lock配合實現的。
我感覺這個Condition和那個差不多,也是用來完成執行緒之間的協作的
但是二者在使用方式上以及功能特性上還是有所差別的。
Object對比Condition:
由此表可以看出,condition介面可以有多個等待佇列,而object監視器方法只有一個佇列,而且還不支援在等待狀態響應中斷,還不支援當前執行緒釋放鎖並進入等待狀態到將來的某個時間。
示例:
也不打算寫新的示例了,用這個Condition介面改造一下之前使用等待通知模式的那個案例吧
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
內容關於:併發程式設計
本文來源於網路,只做技術分享,一概不負任何責任