Java線程:條件變量 lock

    條件變量是Java5線程中很重要的一個概念,顧名思義,條件變量就是表示條件的一種變量。可是必須說明,這裏的條件是沒有實際含義的,僅僅是個標記而已,而且條件的含義每每經過代碼來賦予其含義。
    這裏的條件和普通意義上的條件表達式有着天壤之別。條件變量都實現了java.util.concurrent.locks.Condition接口,條件變量的實例化是經過一個Lock對象上調用newCondition()方法來獲取的,這樣,條件就和一個鎖對象綁定起來了。所以,Java中的條件變量只能和鎖配合使用,來控制併發程序訪問競爭資源的安全。
    條件變量的出現是爲了更精細控制線程等待與喚醒,在Java5以前,線程的等待與喚醒依靠的是Object對象的wait()和notify()/notifyAll()方法,這樣的處理不夠精細。
     而在Java5中,一個鎖能夠有多個條件,每一個條件上能夠有多個線程等待,經過調用await()方法,可讓線程在該條件下等待。當調用signalAll()方法,又能夠喚醒該條件下的等待的線程。有關Condition接口的API能夠具體參考JavaAPI文檔。
條件變量比較抽象,緣由是他不是天然語言中的條件概念,而是程序控制的一種手段。
下面以一個銀行存取款的模擬程序爲例來揭蓋Java多線程條件變量的神祕面紗:

有一個帳戶,多個用戶(線程)在同時操做這個帳戶,有的存款有的取款,存款隨便存,取款有限制,不能透支,任何試圖透支的操做都將等待裏面有足夠存款才執行操做。   java

    若是程序不使用synchronized關鍵字來保證同步,而直接用lock.那麼系統中不存在隱式的同步監視器,就不能使用wait,notify,notifyAll方法進行線程通訊了.但使用Lock對象來保證同步時,Java提供了一個Condition類來保持協調,使用Condition可讓那些已經獲得Lock對象但沒法繼續執行的線程釋放Lock對象,Condition對象也能夠喚醒其餘等待的線程.這狀況下,Lock替代了同步方法或同步代碼塊,Condition替代了同步監視器的功能. 安全

Condition實例綁定在Lock對象上,要得到須要調用Lock對象的newCondition方法.有3個方法:
多線程

  1. await():相似於隱式同步監視器上的wait方法.該await方法有更多的重載方法.
  2. signal():任意喚醒一個Lock對象上等待的單個線程.只有當前線程放棄對該Lock對象的鎖定後(使用await()方法),才能夠自行被喚醒的線程.
  3. signalAll():喚醒全部等待線程。

package com.thread.lock;

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

public class MyCount {
	private String id; // 帳號
	private int cash; // 帳戶餘額
	private Lock lock = new ReentrantLock(); // 帳戶鎖
	private Condition _save = lock.newCondition(); // 存款條件
	private Condition _draw = lock.newCondition(); // 取款條件

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

	/**
	 * 存款
	 * 
	 * @param name
	 *            存款人姓名
	 * @param cash
	 *            存款金額
	 */
	public void saving(String name, int saveCash) {
		lock.lock();
		if (saveCash > 0) {
			cash += saveCash;
			System.out.println(name + " 存款 " + saveCash + ",當前餘額爲" + cash);
		}
		_draw.signalAll(); // 喚醒全部等待線程。
		lock.unlock(); // 釋放鎖
	}

	/**
	 * 取款
	 * 
	 * @param name
	 *            取款人姓名
	 * @param cash
	 *            取款金額
	 */
	public void drawing(String name, int saveCash) {
		lock.lock();
		try {
			if (cash < saveCash) {
				_draw.await();
				// 阻塞取款操做
			} else {
				cash -= saveCash;// 取款
				System.out.println(name + " 取款 " + saveCash + ",當前餘額爲" + cash);
			}
			_save.signalAll(); // 喚醒全部存款操做
		} catch (InterruptedException e) {
			System.out.println(name + " 取款超額了" + " 取款金額爲:" + saveCash);
			// TODO Auto-generated catch block
			// e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
}

class DrawThread extends Thread {
	private String name; // 操做人
	private MyCount myCount; // 帳戶
	private int saveCash; // 存款金額

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

	@Override
	public void run() {
		myCount.drawing(name, saveCash);
	}
}

class SaveThread extends Thread {
	private String name; // 操做人
	private MyCount myCount; // 帳戶
	private int saveCash; // 存款金額

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

	@Override
	public void run() {
		myCount.saving(name, saveCash);
	}

}

package com.thread.lock;

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

public class TestLock {
	public void run(){
		ExecutorService pool = Executors.newFixedThreadPool(5);
		MyCount count = new MyCount("43089183736746383", 1200);
		SaveThread save1 =  new SaveThread("張三",count, 200);
		SaveThread save2 =  new SaveThread("李四",count, 100);
		DrawThread draw1 = new DrawThread("王五", count, 1600);
		DrawThread draw2 = new DrawThread("趙六", count, 500);
		SaveThread save3 =  new SaveThread("王傑",count, 3000);
		pool.execute(save1);
		pool.execute(save2);
		pool.execute(draw1);
		pool.execute(draw2);
		pool.execute(save3);
		pool.shutdownNow();
	}
    public static void main(String[] args) {
		new TestLock().run();
	}
}
相關文章
相關標籤/搜索