java 線程 Lock 鎖使用Condition實現線程的等待(await)與通知(signal)

1、Condition 類
  在前面咱們學習與synchronized鎖配合的線程等待(Object.wait)與線程通知(Object.notify),那麼對於JDK1.5 的
 java.util.concurrent.locks.ReentrantLock 鎖,JDK也爲咱們提供了與此功能相應的類java.util.concurrent.locks.Condition。Condition與重入鎖是經過lock.newCondition()方法產生一個與當前重入鎖綁定的
Condtion實例,咱們通知該實例來控制線程的等待與通知。該接口的全部方法:

複製代碼
public interface Condition {
     //使當前線程加入 await() 等待隊列中,並釋放當鎖,當其餘線程調用signal()會從新請求鎖。與Object.wait()相似。
    void await() throws InterruptedException;

    //調用該方法的前提是,當前線程已經成功得到與該條件對象綁定的重入鎖,不然調用該方法時會拋出IllegalMonitorStateException。
    //調用該方法後,結束等待的惟一方法是其它線程調用該條件對象的signal()或signalALL()方法。等待過程當中若是當前線程被
中斷,該方法仍然會繼續等待,同時保留該線程的中斷狀態。 
    void awaitUninterruptibly();

    // 調用該方法的前提是,當前線程已經成功得到與該條件對象綁定的重入鎖,不然調用該方法時會拋出IllegalMonitorStateException。
    //nanosTimeout指定該方法等待信號的的最大時間(單位爲納秒)。若指定時間內收到signal()或signalALL()則返回nanosTimeout減去已經等待的時間;
    //若指定時間內有其它線程中斷該線程,則拋出InterruptedException並清除當前線程的打斷狀態;若指定時間內未收到通知,
則返回0或負數。 
    long awaitNanos(long nanosTimeout) throws InterruptedException;

    //與await()基本一致,惟一不一樣點在於,指定時間以內沒有收到signal()或signalALL()信號或者線程中斷時該方法會返回
false;其它狀況返回true。
    boolean await(long time, TimeUnit unit) throws InterruptedException;

   //適用條件與行爲與awaitNanos(long nanosTimeout)徹底同樣,惟一不一樣點在於它不是等待指定時間,而是等待由參數指定的
某一時刻。
    boolean awaitUntil(Date deadline) throws InterruptedException;
    
    //喚醒一個在 await()等待隊列中的線程。與Object.notify()類似
    void signal();

   //喚醒 await()等待隊列中全部的線程。與object.notifyAll()類似
    void signalAll();
}
複製代碼
2、使用
一、await()  等待  與 singnal()通知

複製代碼
 1 package com.jalja.org.base.Thread;
 2 
 3 import java.util.concurrent.TimeUnit;
 4 import java.util.concurrent.locks.Condition;
 5 import java.util.concurrent.locks.ReentrantLock;
 6 
 7 /**
 8  * Condition 配合Lock  實現線程的等待 與通知
 9  */
10 public class ConditionTest{
11     public static ReentrantLock lock=new ReentrantLock();
12     public static Condition condition =lock.newCondition();
13     public static void main(String[] args) {
14         new Thread(){
15             @Override
16             public void run() {
17                 lock.lock();//請求鎖
18                 try{
19                     System.out.println(Thread.currentThread().getName()+"==》進入等待");
20                     condition.await();//設置當前線程進入等待
21                 }catch (InterruptedException e) {
22                     e.printStackTrace();
23                 }finally{
24                     lock.unlock();//釋放鎖
25                 }
26                 System.out.println(Thread.currentThread().getName()+"==》繼續執行");
27             }    
28         }.start();
29         new Thread(){
30             @Override
31             public void run() {
32                 lock.lock();//請求鎖
33                 try{
34                     System.out.println(Thread.currentThread().getName()+"=》進入");
35                     Thread.sleep(2000);//休息2秒
36                     condition.signal();//隨機喚醒等待隊列中的一個線程
37                     System.out.println(Thread.currentThread().getName()+"休息結束");
38                 }catch (InterruptedException e) {
39                     e.printStackTrace();
40                 }finally{
41                     lock.unlock();//釋放鎖
42                 }
43             }    
44         }.start();
45     }
46 }
複製代碼
 

執行結果:

Thread-0==》進入等待
Thread-1=》進入
Thread-1休息結束
Thread-0==》繼續執行
流程:在調用await()方法前線程必須得到重入鎖(第17行代碼),調用await()方法後線程會釋放當前佔用的鎖。同理在調用
signal()方法時當前線程也必須得到相應重入鎖(代碼32行),調用signal()方法後系統會從condition.await()等待隊列中喚醒一
個線程。當線程被喚醒後,它就會嘗試從新得到與之綁定的重入鎖,一旦獲取成功將繼續執行。因此調用signal()方法後必定要釋放當
前佔用的鎖(代碼41行),這樣被喚醒的線程纔能有得到鎖的機會,才能繼續執行。

3、JDK中對Condition 的使用
  咱們來看看java.util.concurrent.ArrayBlockingQueue;

  基於數組的阻塞隊列實現,在ArrayBlockingQueue內部,維護了一個定長數組,以便緩存隊列中的數據對象,這是一個經常使用的阻塞
隊列,除了一個定長數組外,ArrayBlockingQueue內部還保存着兩個整形變量,分別標識着隊列的頭部和尾部在數組中的位置。

  看看他的put方法:

複製代碼
  public void put(E e) throws InterruptedException {
        checkNotNull(e);//對傳入元素的null判斷
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();//對put()方法作同步
        try {
            while (count == items.length)//若是隊列已滿
                notFull.await();//讓當前添加元素的線程進入等待狀態
            insert(e);// 若是有其餘線程調用signal() 通知該線程 ,則進行添加行爲
        } finally {
            lock.unlock();//釋放鎖
        }
    }
    
    private E extract() {
        final Object[] items = this.items;
        E x = this.<E>cast(items[takeIndex]);
        items[takeIndex] = null;
        takeIndex = inc(takeIndex);
        --count;
        notFull.signal();//喚醒一個在Condition等待隊列中的線程
        return x;
    }
相關文章
相關標籤/搜索