1. 程式人生 > >Java線程與並發編程實踐----鎖框架

Java線程與並發編程實踐----鎖框架

等待 for sleep exec 實現 return 詞法 rgs com

Java.util.concurrent.locks包提供了一個包含多種接口和類的框架,它

針對條件進行加鎖和等待。不同於對象的內置加鎖同步以及java.lang.Object的等

待/通知機制,包含鎖框架的並發工具類通過輪詢鎖、顯示等待及其它方式改善這種

機制。

鎖框架包含了經常使用的鎖、重入鎖、條件、讀寫鎖以及沖入讀寫鎖等類別。

一、鎖(Lock)

Lock 實現提供了比使用 synchronized 方法和語句可獲得的更廣泛的鎖定操作。此實

現允許更靈活的結構,可以具有差別很大的屬性,可以支持多個相關的 Condition 對象。

鎖是控制多個線程對共享資源進行訪問的工具。通常,鎖提供了對共享資源的獨占訪問。

一次只能有一個線程獲得鎖,對共享資源的所有訪問都需要首先獲得鎖。不過,某些鎖

可能允許對共享資源並發訪問,如 ReadWriteLock 的讀取鎖。

synchronized 方法或語句的使用提供了對與每個對象相關的隱式監視器鎖的訪問,但卻

強制所有鎖獲取和釋放均要出現在一個塊結構中:當獲取了多個鎖時,它們必須以相反的

順序釋放,且必須在與所有鎖被獲取時相同的詞法範圍內釋放所有鎖。

雖然 synchronized 方法和語句的範圍機制使得使用監視器鎖編程方便了很多,而且還

幫助避免了很多涉及到鎖的常見編程錯誤,但有時也需要以更為靈活的方式使用鎖。

例如,某些遍歷並發訪問的數據結果的算法要求使用 "hand-over-hand" 或 "chain locking":

獲取節點 A 的鎖,然後再獲取節點 B 的鎖,然後釋放 A 並獲取 C,然後釋放

B 並獲取 D,依此類推。Lock 接口的實現允許鎖在不同的作用範圍內獲取和釋放,

並允許以任何順序獲取和釋放多個鎖,從而支持使用這種技術。

二、重入鎖

類ReentrantLock實現了接口Lock,描述了一個可重入的互斥鎖。這個鎖和一個持有量

相關聯。當一個線程持有這個鎖並且調用lock()、lockUninterruptibly()或者任意一個trylock()

方法重新獲取鎖,這個持有量就遞增一。當線程調用unlock()方法,持有量就遞減1。當持有量

降為零,鎖就會被釋放。

ReentrantLock提供了與通過同步方法、代碼塊得以訪問的隱士監聽鎖同樣的並發語義

及內存語義。不過它具備可擴展的功能並且在高線程爭用的環境下(線程頻繁地請求獲取已經

被其它線程持有的鎖)具有更好地性能。

使用重入鎖實現同步塊:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Test {

	public static void main(String[] args) {
		
		final Lock lock = new ReentrantLock();
		ExecutorService execute = Executors.newFixedThreadPool(2);
		class Worker implements Runnable {
			
			private final String name;
			public Worker(String name) {
				this.name = name;
			}

			@Override
			public void run() {
				lock.lock();
				System.out.println(Thread.currentThread().getName() +"---"+ name);
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					lock.unlock();
				}
			}
		}
		execute.execute(new Worker("jiaxx"));
		execute.execute(new Worker("jiadd"));
		execute.shutdown();
	}
}

三、條件

接口Condition把Object的wait和notification方法分解到不同的條件對象中。

通過把這些條件和任意Lock實現的使用組合起來,起到讓每個對象上具有多重等待集合

的作用。這裏Lock取代了同步方法、代碼塊,Condition取代了Object的wait、notification

方法。

使用重入鎖和條件實現生產者和消費者:

package xiancheng;

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

public class PC {

	public static void main(String[] args) {
		Shared s = new Shared();
		Thread t1 = new Thread(new Product(s,s.lock,s.condition));
		Thread t2 = new Thread(new Consumer(s,s.lock,s.condition));
		t1.start();
		t2.start();
	}
}

class Shared {

	private char c;
	private volatile boolean writeable = true;
	final Lock lock;
	final Condition condition;

	public Shared() {
		this.lock = new ReentrantLock();
		this.condition = lock.newCondition();
	}

	public void setChar(char ch) {
		lock.lock();
		while (!writeable) {
			try {
				condition.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.c = ch;
		writeable = false;
		condition.signal();
		lock.unlock();
	}

	public char getChar() {
		lock.lock();
		while (writeable) {
			try {
				condition.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		writeable = true;
		condition.signal();
		lock.unlock();
		return c;
	}
}

class Product implements Runnable {

	private final Shared s;
	private final Lock lock;
	private final Condition condition;

	public Product(Shared s, Lock lock, Condition condition) {
		this.s = s;
		this.lock = lock;
		this.condition = condition;
	}

	@Override
	public void run() {
		for (char i = 'A'; i < 'Z'; i++) {
			lock.lock();
			s.setChar(i);
			System.out.println("生產者生產了一個" + i);
			lock.unlock();
		}
	}

}

class Consumer implements Runnable {

	private final Lock lock;
	private final Condition condition;
	private final Shared s;

	public Consumer(Shared s, Lock lock, Condition condition) {
		this.s = s;
		this.lock = lock;
		this.condition = condition;
	}

	@Override
	public void run() {
		char ch;
		do {
			lock.lock();
			ch = s.getChar();
			System.out.println("消費者消費了一個" + ch);
			lock.unlock();
		} while (ch != 'Z');
	}
}

四、讀寫鎖

讀寫鎖適用於對數據結構頻繁讀而較少修改的場景。

鎖框架針對這些場景提供了讀--寫鎖機制,在讀取時具有更好地並發性,而寫入時

保證安全的互斥訪問。這一機制基於接口ReadWriteLock接口。該接口維護了一對鎖,一個

針對寫操作,一個針對讀操作,讀鎖可以被多個線程持有,而寫鎖是互斥的,只允許一個線程

訪問修改共享數據。

具體代碼的實現參見我的另一篇微博:

http://blog.51cto.com/12222886/1964183

Java線程與並發編程實踐----鎖框架