標籤:
一、初識Concurrent
第一次看見concurrent的使用是在同事寫的一個抽取系統代碼裡,當時這部分代碼沒有完成,有許多的問題,另一個同事接手了這部分代碼的功能開發,由於他沒有多線程開發的經驗,所以我就一起幫著分析。最開始看到這個時很煩燥啊,因為自己接觸java時間很短,連synchronized都不知道怎麼用呢,突然發現有這麼個複雜的東西。當時就只好開始學習吧,畢竟是使用嘛,第一目的就是瞭解清楚這玩意的各個類與方法都幹嘛用的,然後看了看同事的代碼大概也就清楚了。感覺這和大部分人一樣,能用就行。
下面是一段其中的應用
public class VoiceExtractRunnable implements Runnable { @Override public void run() { // 建立線程池 ExecutorService service = Executors.newCachedThreadPool(); // 聲明儲存各任務(線程)執行結果的集合 List<Future<VoiceExtractExpertInRuleVO>> futures = new ArrayList<Future<VoiceExtractExpertInRuleVO>>( rules.size()); // 迴圈提交任務 for (VoiceExtractExpertInRuleVO ruleVO : rules) { ruleVO.setExtractParamVO(vo); futures.add(service.submit(new VoiceRuleCallable(ruleVO))); } for (Future<VoiceExtractExpertInRuleVO> f : futures) { try { VoiceExtractExpertInRuleVO r = f.get(); returnRules.add(r); // 擷取任務執行完成後的返回結果 } catch (Exception e) { logger.error("", e); } } // 所有任務都執行完畢後,關閉線程池 service.shutdown(); }}
看完這個代碼(省略了大部分的商務邏輯代碼)就能感覺到一個優點,就是線程的執行和結果擷取是可以非同步,這樣對於開發來說確實是有很大的協助。代碼結構也比較清晰。
但這個時候我主要還是在能用就行的階段,而且也並不關心concurrent裡到底有多少重要的代碼。最近在學習JAVA的基礎知識,看到安全執行緒的時候Concurrent開始進入我的視野,這時我才知道它原來是這麼豐富,所以才開始一點點的瞭解。這個過程中也一直感覺到寫這些代碼的工程師確實厲害,能在實踐的過程中總結出這麼好的代碼,供廣大的開發們使用。
二、主要的類
Executor :具體Runnable任務的執行者。ExecutorService :一個線程池管理者,其實作類別有多種,我會介紹一部分。我們能把Runnable,Callable提交到池中讓其調度。Semaphore :一個計數訊號量ReentrantLock :一個可重新進入的互斥鎖定 Lock,功能類似synchronized,但要強大的多。Future :是與Runnable,Callable進行互動的介面,比如一個線程執行結束後取返回的結果等等,還提供了cancel終止線程。BlockingQueue :阻塞隊列。CompletionService : ExecutorService的擴充,可以獲得線程執行結果的CountDownLatch :一個同步輔助類,在完成一組正在其他線程中執行的操作之前,它允許一個或多個線程一直等待。 CyclicBarrier :一個同步輔助類,它允許一組線程互相等待,直到到達某個公用屏障點 Future :Future 表示非同步計算的結果。ScheduledExecutorService :一個 ExecutorService,可安排在給定的延遲後運行或定期執行的命令。
上面是一些常用類,參考這個文章,寫的比較清楚:http://www.cnblogs.com/aurawing/articles/1887056.html
我個人看了代碼後對下面的這些類印象比較深刻:
ConcurrentHashMap:支援並發的hashMapAbstractQueuedSynchronizer:提供了同步鎖的基礎功能實現,包含獨佔鎖和共用鎖定
對於ConcurrentHashMap這個類是一種適應於並發情境下的hashMap,是建立在分離鎖基礎上。其內部結構可以劃分為N個段,每個段都有自己的並發鎖,這樣寫入時可以寫入不同的段中,從而提高了並發的效能。
參考:http://blog.csdn.net/fw0124/article/details/43308193
AbstractQueuedSynchronizer是Concurrent包中對鎖的關鍵抽象實現,主要是提供了一個state欄位用於控制並發,通過控制state的原子狀態從而保證多線程時的鎖機制。這個類是抽象類別,很多的情境實現需要在特定的子類中實現。
參考:
http://www.infoq.com/cn/articles/jdk1.8-abstractqueuedsynchronizer
http://www.infoq.com/cn/articles/java8-abstractqueuedsynchronizer
三、學習到的一些知識點
這幾天在看原始碼中也不斷的在網上尋找各種資料,還是學到了不少東西。
分拆鎖(lock spliting)就是若原先的程式中多處邏輯都採用同一個鎖,但各個邏輯之間又相互獨立,就可以拆(Spliting)為使用多個鎖,每個鎖守護不同的邏輯。
分拆鎖有時候可以被擴充,分成可大可小加鎖塊的集合,並且它們歸屬於相互獨立的對象,這樣的情況就是分離鎖(lock striping)。(摘自《Java並發編程實踐》)
對於分離鎖有個更好些的解釋:分拆鎖有時候可以被擴充,分成若干加鎖塊的集合,並且它們歸屬於相互獨立的對象,這樣的情況就是分離鎖。例如,ConcurrentHashMap 的實現使用了一個包含 16 個鎖的數組,每一個鎖都守護 HashMap 的 1/16 。假設 Hash 值均勻分布,這將會把對於鎖的請求減少到約為原來的 1/16 。這項技術使得 ConcurrentHashMap 能夠支援 16 個的並發 Writer 。當多處理器系統的大負荷訪問需要更好的並發性時,鎖的數量還可以增加。——摘自developerworks
sun.misc.Unsafe 是一個封裝了很多底層操作的類,但是網上沒找到太多的資料,但在Concurrent包中用的比較多,最為關鍵的是其提供的方法compareAndSwap之類的方法是原子的,可以不用自己加鎖。看了Concurrent包中的鎖主要是通過這個方法來實現的鎖狀態管理。
但網上也提到他可以操作記憶體,也難怪叫Unsafe這名,如果在java代碼裡隨便用的話那Java不就變成和C++差不多了,呵呵。所以除了在JDK裡的單元,自己寫的代碼中不能直接使用這個類。
volatile關鍵字:這個關鍵字是要求多線程環境下訪問受volatile修改的共用資料時具有可見度。
我確實不知道怎麼解釋它,推薦兩篇不錯的文章:
http://www.ibm.com/developerworks/cn/java/j-jtp06197.html
http://www.cnblogs.com/dolphin0520/p/3920373.html
四、小感慨
在看著Concurrent包裡的代碼時,確實對一些精秒的設計很感歎,比如鎖的設計,一種抽象與實現結合的良好設計。裡面的許多小細節都體現了技術的功底,反想自己為什麼設計不出這樣的代碼。
我感覺兩方面:
1、沒有實際的問題要去解決
比如Concurrent這裡面的代碼針對並發的編程,說實話工作中遇到的不多,一般的情況用用synchronized也是可以解決的,以前在.net裡也就用用lock關鍵字。delphi用的時候也簡單。所以沒有一個好的工作情境讓你去解決這些問題,說實話想都想不到。
2、基礎不紮實
其實看了許多代碼都是些基礎應用,你說流、檔案、並發這些東西都是電腦裡都要面對的問題,只要掌握了這些知識,其實在實際遇到問題的時候就可以用上了。否則就會當作難題用一些其他方法規避掉,反而失去了寫出更好代碼的機會,時間長了就變的平庸
以後還是要多多努力學習基礎,這一段時間以來我覺得自己可以在編程上有更多的收穫,或許我真的能寫代碼到50歲,至少我覺得50歲的時候還是可以跟上時代,寫出優秀的代碼。
學習筆記:java並發編程學習之初識Concurrent