天天看點

JDK1.5 Condition接口

Condition 将 Object 螢幕方法(wait、notify和 notifyAll)分解成截然不同的對象,以便通過将這些對象與任意 Lock 實作組合使用,為每個對象提供多個等待 set (wait-set)。其中,Lock 替代了 synchronized 方法和語句的使用,Condition 替代了 Object 螢幕方法的使用。

條件(也稱為條件隊列或條件變量)為線程提供了一個含義,以便在某個狀态條件現在可能為 true 的另一個線程通知它之前,一直挂起該線程(即讓其“等待”)。因為通路此共享狀态資訊發生在不同的線程中,是以它必須受保護,是以要将某種形式的鎖定與該條件相關聯。等待提供一個條件的主要屬性是:以原子方式釋放相關的鎖定,并挂起目前線程,就像 Object.wait 做的那樣。

Condition 執行個體實質上被綁定到一個鎖定上。要為特定 Lock 執行個體獲得 Condition 執行個體,請使用其 newCondition() 方法。

作為一個示例,假定有一個綁定的緩沖區,它支援 put 和 take 方法。如果試圖在空的緩沖區上執行 take 操作,則在某一個項變得可用之前,線程将一直阻塞;如果試圖在滿的緩沖區上執行 put 操作,則在有空間變得可用之前,線程将一直阻塞。我們喜歡在單獨的等待 set 中儲存 put 線程和 take 線程,這樣就可以在緩沖區中的項或空間變得可用時利用最佳規劃,一次隻通知一個線程。可以使用兩個 Condition 執行個體來做到這一點。

 class BoundedBuffer {

   final Lock lock = new ReentrantLock();

   final Condition notFull  = lock.newCondition();

   final Condition notEmpty = lock.newCondition();

   final Object[] items = new Object[100];

   int putptr, takeptr, count;

   public void put(Object x) throws InterruptedException {

     lock.lock();

     try {

       while (count == items.length)

         notFull.await();

       items[putptr] = x;

       if (++putptr == items.length) putptr = 0;

       ++count;

       notEmpty.signal();

     } finally {

       lock.unlock();

     }

   }

   public Object take() throws InterruptedException {

     lock.lock();

     try {

       while (count == 0)

         notEmpty.await();

       Object x = items[takeptr];

       if (++takeptr == items.length) takeptr = 0;

       --count;

       notFull.signal();

       return x;

     } finally {

       lock.unlock();

     }

   }

 }

(ArrayBlockingQueue類提供了這項功能,是以沒有理由去實作這個示例類。)

Condition 實作可以提供不同于 Object 螢幕方法的行為和語義,比如受保證的通知排序,或者在執行通知時不需要保持一個鎖定。如果某個實作提供了這樣特殊的語義,則該實作必須記錄這些語義。

注意,Condition 執行個體隻是一些普通的對象,它們自身可以用作 synchronized 語句中的目标,并且可以調用自己的 wait 和 notification 螢幕方法。擷取 Condition 執行個體的螢幕鎖定或者使用其螢幕方法,與擷取和該 Condition 相關的 Lock 或使用其 waiting 和 signalling 方法沒有什麼特定的關系。為了避免混淆,建議除了在其自身的實作中之外,切勿以這種方式使用 Condition 執行個體。

執行個體:

package com.bijian.thread;

public class SaveThread extends Thread {

	private String name; // 操作人
	private MyCount myCount; // 賬戶
	private int x; // 存款金額

	public SaveThread(String name, MyCount myCount, int x) {
		this.name = name;
		this.myCount = myCount;
		this.x = x;
	}

	public void run() {
		myCount.saving(x, name);
	}
}
           
package com.bijian.thread;

public class DrawThread extends Thread {

	private String name; // 操作人
	private MyCount myCount; // 賬戶
	private int x; // 存款金額

	DrawThread(String name, MyCount myCount, int x) {
		this.name = name;
		this.myCount = myCount;
		this.x = x;
	}

	public void run() {
		myCount.drawing(x, name);
	}
}
           
package com.bijian.thread;

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

public class MyCount {

	private String oid; // 賬号
	private int cash; // 賬戶餘額
	private Lock lock = new ReentrantLock(); // 賬戶鎖
	private Condition _save = lock.newCondition(); // 存款條件
	private Condition _draw = lock.newCondition(); // 取款條件

	MyCount(String oid, int cash) {
		this.oid = oid;
		this.cash = cash;
	}

	public void saving(int x, String name) {
		lock.lock(); // 擷取鎖
		try {
			if (x > 0) {
				cash += x; // 存款
				System.out.println(name + "存款" + x + ",目前餘額為" + cash);
			}
			_draw.signalAll(); // 喚醒所有等待線程。
		} finally {
			lock.unlock();
		}
	}

	public void drawing(int x, String name) {
		lock.lock(); // 擷取鎖
		try {
			if (cash - x < 0) {
				_draw.await(); // 阻塞取款操作
			} 
			cash -= x; // 取款
			System.out.println(name + "取款" + x + ",目前餘額為" + cash);
			
			_save.signalAll(); // 喚醒所有存款操作
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock(); // 釋放鎖
		}
	}
}
           
package com.bijian.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {

	public static void main(String[] args) {
		
		//建立并發通路的賬戶
		MyCount myCount = new MyCount("95599200901215522", 10000);
		//建立一個線程池
		ExecutorService pool = Executors.newFixedThreadPool(2);
		Thread t1 = new SaveThread("張三", myCount, 2000);
		Thread t2 = new SaveThread("李四", myCount, 3600);
		Thread t3 = new DrawThread("王五", myCount, 2700);
		Thread t4 = new SaveThread("老張", myCount, 600);
		Thread t5 = new DrawThread("老牛", myCount, 1300);
		Thread t6 = new DrawThread("胖子", myCount, 800);
		//執行各個線程
		pool.execute(t1);
		pool.execute(t2);
		pool.execute(t3);
		pool.execute(t6);
		pool.execute(t5);
		pool.execute(t4);
		//關閉線程池
		pool.shutdown();
	}
}
           

運作結果:

張三存款2000,目前餘額為12000
李四存款3600,目前餘額為15600
王五取款2700,目前餘額為12900
胖子取款800,目前餘額為12100
老張存款600,目前餘額為12700
老牛取款1300,目前餘額為11400