1. 程式人生 > >Java線程與並發編程實踐----額外的並發工具類

Java線程與並發編程實踐----額外的並發工具類

pro 應該 多線程操作 它的 使用 som ron java 如果

一、並發集合

java.util包下提供了很多的集合類,如ArrayList、TreeSet、HashMap,但是這些

集合都是非線程安全的,並且對於單列集合的叠代器,采用的是快速失敗機制,當正在叠代

遍歷的集合被其它線程修改時,便會拋出 java.util.ConcurrentModificationException。

這顯然對於多線程操作的集合是十分不方便的,但早Colections這個工具類中有方法可以返回

線程安全的集合,然而這種集合對於高並發環境,性能十分低下。

於是,java.util.concurrent包下提供了很多相關集合的類,即並發集合,這些集合

具有並發性能和高擴展性,並且返回的是弱一致性的叠代器,即不再是快速失敗機制的叠代器。

大致描述如上,具體翻查jdk。下面單獨研究使用並發集合完成生產者消費者的問題。

使用BlockingQueue和ArrayBlockingQueue實現生產者消費者:

ArrayBlockingQueue是一個由數組支的有界阻塞隊列,當隊列滿載時,put()方法會阻塞,當

隊列為空時,take()方法阻塞,因此用這種隊列可以很輕松的實現消費者、生產者。

轉到http://blog.51cto.com/12222886/1963884

深入學習ConcurrentHashMap:

技術分享圖片

重點了解此方法。

ConcurrentHashMap是一個線程安全的集合,但是在多線程德操作之下它並不是線程安全

的,例如如下代碼:

if(!map.containsKey("something")) 
			map.put("something", "something");

試想,當我們再判斷完之後,還未put之前,有一條線程向map中put進一個鍵為something的值,

然後我們再繼續put,那麽剛才的那個值是不是就被覆蓋了?這就產生了安全問題。。為了保證

安全我們應該這樣做:

synchronized (map) {
			if (!map.containsKey("something"))
				map.put("something", "something");
		}

以上可以解決安全問題,但是性能會被降低。。我們應該使用ConcurrentHashMap所提供的

一個API,putIfabsent(K key,V value),它的含義是,假如鍵值不存在,便put進去,它是線程

安全的,並且性能更高,相當於如下代碼:

synchronized (map) {
			if (!map.containsKey("something"))
				return map.put("something", "something");
			else
				return map.get(key);
		}

二、原子變量

和對象監聽器關聯的那些內置鎖一直以來都有性能不佳的問題,後來出現了

很多非阻塞算法,可大大提高性能和擴展性。

java.util.concurrent.atomic提供了高效非阻塞算法。它支持單個變量可進行無

鎖及線程安全的操作。有如下原子類:

技術分享圖片

原子變量用於實現計數器、序列生成器以及其它構造。在線程高爭用的環境下,這些構造要求互斥

而不影響性能。假如有如下代碼:

package xiancheng;

public class ID {

	private static volatile long nextID = 1;
	
	static synchronized long getNextID() {
		return nextID++;
	}
}

上述代碼中volatile保證了可見性,synchronized保證了互斥性,在多線程環境下,上述代碼沒有

問題,然而在高爭用的環境中性能會很低。我們可以用原子變量代替上述代碼:

class ID2{
	private static AtomicLong nextID = new AtomicLong();
	
	public static long getNextID() {
		return  nextID.getAndIncrement();
	}
}

上述代碼完全保證了可見性,互斥性以及操作的原子性,並且由於它實現了高爭用下的非阻塞算法,

因此它的性能相對來說高了很多。

那麽問題來了,為什麽原子變量能夠提高性能????

Compare-and-swap(CAS機制)

Compare-and-swap(CAS機制)是一個針對非搶占式微處理器的一條指定指令的寬泛術語,這條

指令讀取內存的位置,比較讀到的值和期望的值,當讀到值和期望值匹配時,就將新值存儲到該內存

位置,否則,什麽也不發生。

CAS支持原子的讀-改-寫序列,通常這樣使用:

(1)從地址A讀取x

(2)在x進行多步計算

(3)使用CAS將A值從x變為y。在進行這些操作時,如果A值沒有改變,CAS就成功了

那CAS到底優越之處在哪呢?

package xiancheng;

public class ID {

	private static volatile long nextID = 1;
	
	static synchronized long getNextID() {
		return nextID++;
	}
}

上面的代碼使用synchronized,高爭用環境下的監聽鎖會導致過多的上下文切換,這樣會阻礙所有線程

並且導致應用程序無法很好的擴展。而CAS機制不適用監聽器來是操作原子化,而是在修改ID的值之前會

進行判斷,如果該值沒發生過變化,就將新值賦值給該變量,如果發生變化了就什麽也不做,而在這中間

對值是否發生過變化的判斷是利用CAS指令完成的。

java.util.concurrent.locks.ReentrantLock就使用了CAS機制改善了性能,原子類也利用了CAS機制。


Java線程與並發編程實踐----額外的並發工具類