1. 程式人生 > >java多執行緒程式設計之讀寫鎖設計高效能快取器

java多執行緒程式設計之讀寫鎖設計高效能快取器

解決多執行緒執行緒安全問題的主要方法是通過加鎖的方式來實現,當多個執行緒對某個變數進行讀取或寫入的時候通過加鎖來限定只有當前獲取鎖許可權的執行緒才可以對資料進行讀寫,當該執行緒訪問完畢釋放鎖之後其他阻塞執行緒才擁有訪問許可權,當下一個執行緒得到執行許可權的時候同樣進行上述操作步驟。而實現加鎖的方式最簡單的有兩種,一個是通過關鍵字synchronized來對整個方法或部分程式碼塊進行加鎖,另外一種是用Lock物件來實現執行緒互斥,保證執行緒安全。對於一般系統來講,加鎖的方式在很大程度上已經滿足了系統需要,但是當系統面臨高併發請求的時候這種加鎖方式顯然不能解決系統效率問題,為此通過更高效能的讀寫鎖並配合快取系統解決效能問題成為了首選方案。

為什麼會出現讀寫鎖?為什麼使用讀寫鎖的效能會更高一些?這是因為在傳統的synchronized關鍵字或Lock物件加鎖的時候是不區分是讀操作還是鎖操作的,只要有執行緒進入之後就會對該段程式碼進行鎖定直到執行緒釋放鎖,考慮一下在具體的應用環境中通常出現最多的並不是大量的寫操作,而是讀操作,如果僅僅是讀操作不涉及到共享資料的寫操作是沒有必要進行加鎖行為的,一旦加了鎖之後大量的讀操作只能按佇列順序依次讀取,這就好比是雙車道的馬路加鎖之後變成了單車道,會造成不必要的效能上的損失。但是我們又不得不考慮大量讀操作中夾雜寫操作的情況,一旦未對寫操作進行加鎖,又很可能造成執行緒上的不安全問題。所以為了兼顧系統性能和執行緒安全,讀寫鎖應運而生。由於讀鎖是排寫鎖操作的,但是讀鎖並不排讀鎖操作,多個讀鎖可以併發不阻塞所以它可以很好的應對高併發的讀操作,同時寫鎖又是拍寫鎖操作和排讀寫操作的,即當某個執行緒加上讀鎖之後,在該鎖釋放之前其他執行緒是不能進行讀操作更不能進行寫操作的,這樣就解決了執行緒的安全問題。下面通過例項介紹一下如何通過讀寫鎖設計高效能的快取系統。

public class CacheContainer {

	private Map<String, Object> cache = new HashMap<String, Object>();

	private ReadWriteLock rwl = new ReentrantReadWriteLock();
	public  Object getData(String key){
		rwl.readLock().lock();
		Object value = null;
		try{
			value = cache.get(key);
			if(value == null){
				rwl.readLock().unlock();
				rwl.writeLock().lock();
				try{
					if(value==null){
						value = "test";//查詢資料庫操作後更新資料
                                                cache.put(key,value);
                                        }
				}finally{
					rwl.writeLock().unlock();
				}
				rwl.readLock().lock();
			}
		}finally{
			rwl.readLock().unlock();
		}
		return value;
	}
}

分析上面快取器程式碼可以看出當多個執行緒從快取中取資料的時候首先加上讀鎖,這樣可以保證多個執行緒之間併發的執行讀操作,然後根據key值從試圖從容器中獲取對應的value,當獲取的value值存在的時候將該值返回,這種情況相當於沒有加鎖。但是當某個執行緒首先到達讀操作的時候發現value沒有對應的值,此時需要進行的是從資料庫中讀取資料並將該資料放入快取中,在讀資料庫並更新資料的時候就必須需要加寫鎖來保證在寫操作的排他性。