Condition接口
Condition是一個接口,其提供的就兩個核心方法,await和signal方法。分別對應着Object的wait和notify方法。調用Object對象的監視器方法的這兩個方法,
需要在同步代碼塊裏面,即必須先獲取到鎖才能執行這兩個方法。同理,Condition調用這兩個方法,也必須先獲取到鎖,與Lock配合可以實現等待/通知模式,但是這兩者在使用方式以及功能特性上還是有差別的。
Object的監視器方法與Condition接口的對比
Condition簡單用法
Condition定義了等待/通知兩種類型的方法,當前線程調用這些方法時需要提前獲取到Condition對象關聯的鎖。Condition對象是由Lock對象(調用Lock對象的newCondition()方法)創建出來的,換句話說Condition是依賴Lock對象的。Condition的使用方式比較簡單,需要注意在調用方法前獲取鎖.如下面代碼所示,一般都會將Condition對象作爲成員變量。當調用await()方法後,當前線程會釋放鎖並在此等待,而其他線程調用Condition對象的signal()方法,通知當前線程後,當前線程才從await()方法返回,並且在返回前已經獲取了鎖。
package com.brian.mutilthread.condition; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @Slf4j public class ConditionDemo { private static Lock lock = new ReentrantLock(); private static Condition condition = lock.newCondition(); public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(ConditionDemo::run); thread.start(); try { Thread.sleep(1000); } catch (Exception e) { } lock.lock(); // 喚醒 condition.signal(); lock.unlock(); log.info(" === {} ===: {} 33333",Thread.currentThread().getName()); } private static void run() { lock.lock(); try { log.info("=== {} ===: {} 11111", Thread.currentThread().getName()); // 等待 condition.await(); log.info("=== {} ===: {} 22222", Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } }
Condition常用API
手寫基於condition的隊列
package com.brian.mutilthread.condition; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @Slf4j public class BrianQueue<T> { private Object[] items; // 添加的下標,刪除的下標和數組當前數量 private int addIndex, removeIndex, count; private Lock lock = new ReentrantLock(); private Condition emptyCondition = lock.newCondition(); private Condition fullCondition = lock.newCondition(); public BrianQueue(int size) { items = new Object[size]; } // 添加一個元素,如果數組滿,則添加線程進入等待狀態,直到有"空位" public void add(T t) throws InterruptedException { lock.lock(); try { while (count == items.length) { log.info("===== queue is full and be blocked ======"); fullCondition.await(); } items[addIndex] = t; if (++addIndex == items.length) { addIndex = 0; } log.info("=== add() ===: {}", addIndex); ++count; emptyCondition.signal(); } finally { lock.unlock(); } } // 由頭部刪除一個元素,如果數組空,則刪除線程進入等待狀態,直到有新添加元素 @SuppressWarnings("unchecked") public T remove() throws InterruptedException { lock.lock(); try { while (count == 0) { log.info("===== queue is empty and be blocked ======"); emptyCondition.await(); } Object x = items[removeIndex]; if (++removeIndex == items.length) { removeIndex = 0; } log.info("=== remove() ===: {}", removeIndex); --count; fullCondition.signal(); return (T) x; } finally { lock.unlock(); } } }
測試類
package com.brian.mutilthread.condition; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @Slf4j public class BrianQueueDemo { public static void main(String[] args) throws InterruptedException { BrianQueue<Integer> brianQueue = new BrianQueue<>(5); ExecutorService executorService = Executors.newFixedThreadPool(2); executorService.execute(()->{ Integer num = 0; while (true){ try { brianQueue.add(++num); } catch (InterruptedException e) { e.printStackTrace(); } } }); executorService.execute(()->{ while (true){ try { brianQueue.remove(); } catch (InterruptedException e) { e.printStackTrace(); } } }); } }
Condition await() 和 signal()源碼解讀
此處以Condition的實現類ConditionObject,ConditionObject是同步器AbstractQueuedSynchronizer的內部類來分析
public final void await() throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); // 將當前節點添加到最後一個節點 Node node = addConditionWaiter(); //釋放鎖的狀態 long savedState = fullyRelease(node); int interruptMode = 0; while (!isOnSyncQueue(node)) { LockSupport.park(this); if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; } //重新獲取鎖 if (acquireQueued(node, savedState) && interruptMode != THROW_IE) interruptMode = REINTERRUPT; if (node.nextWaiter != null) // clean up if cancelled unlinkCancelledWaiters(); if (interruptMode != 0) reportInterruptAfterWait(interruptMode); }
調用Condition的await()方法(或者以await開頭的方法),會使當前線程進入等待隊列並釋放鎖,同時線程狀態變爲等待狀態。當從await()方法返回時,當前線程一定獲取了Condition相關聯的鎖。如果從隊列(同步隊列和等待隊列)的角度看await()方法,當調用await()方法時,相當於同步隊列的首節點(獲取了鎖的節點)移動到Condition的等待隊列中。
public final void signal() { if (!isHeldExclusively()) throw new IllegalMonitorStateException(); //獲取單向鏈表, Node first = firstWaiter; if (first != null) doSignal(first); }
調用Condition的signal()方法,將會喚醒在等待隊列中等待時間最長的節點(首節點),在喚醒節點之前,會將節點移到同步隊列中。