《虛擬機併發編程》讀書筆記(一)

出差北京,趁回杭州以前去了趟海淀,順手帶了本以前一直想買的虛擬機併發編程,雖然這本書在amazon中國上的評價通常,但看看總仍是有些收穫吧,可能那些說書通常的都是大神吧。書應該是剛上新的,放在了最顯眼的位置。一晚上火車,看了前三章以爲不錯,仍是忍不住寫點兒筆記。 java

書的前言部分推薦了兩本書《Java Concurrency in Practice》和《Concurrent Programming in Java》,這兩本書主要對JDK提供解決方案,並在內存模型和一致性上進行了比較全面的敘述。而《虛擬機併發編程》則更重於對實際問題的分析和一些經驗性的結論。好比這本書會從IO密集型與計算密集型等方面對並行程序進行分析。 編程

書主要描述了三種併發的解決方案,java JDK併發模型,軟件事務內存(STM)和基於角色的併發模型(Actor-based)。說來慚愧,這幾個中文名詞我還真沒有聽過。書主要分五部分,第一部分仍是對併發的概念、應用場合等基礎方面進行講解,這部分通俗易懂,並且選用的實例很是具備表明性。後面幾部分即JDKK併發模型、STMActor-based和後記。今天這部分主要記錄前三章的內容,關於併發策略。 多線程

在講到Race Condition的時候,做者用到了一個因爲JIT編譯器優化而產生的併發問題,固然這也跟java內存模型相關,可是這樣的例子在以前的書確實不多見。書上給出了一個簡短的程序: 併發

public class RaceCondition {
	private static boolean done;
	
	public static void main(String[] args) throws InterruptedException {
		new Thread(new Runnable(){
			@Override
			public void run() {
				int i=0;
				while(!done){
					i++;
				}
				System.out.println("Done!");
			}
		}).start();
		
		System.out.println("OS:"+System.getProperty("os.name"));
		Thread.sleep(2000);
		done=true;
		System.out.println("flag done set to true");
	}
}
這個程序在JVM server模式下和client模式下運行的結果會不一樣,在client模式下,程序能夠執行到Done,而server模式則不行。做者用的是JDK1.6的環境,很惋惜我在1.7版本的JDK上並無成功的運行處相應的結果。固然個人機器是64位的,書中給出的是32位的機器。


咱們暫且運用做者給出的結論,這是因爲server JIT編譯器優化所致使的。JIT編譯器可能對新線程代碼裏的while循環進行優化,致使新線程在線程上下文中不能看到變量done的變化。還有可能會是新線程只會從寄存器或本地cache中讀取done的值。這均可以歸結爲內存柵欄問題,具體可看wiki百科,有很詳細的解釋。說到這裏,是否是發現這個問題和內存可見性有關了,若是能想到這一步,那解決方案其實就很簡單了,在done前面加volatile修飾,讓done再也不付線程中作cache,內存都去主內存中讀取。這多是個挺巧的問題,因爲編譯器的優化形成了內存可見性的問題。關於內存可見性,在《Java Concurrency in Practice》中有很詳細的描述。 ide

接下去的第二章叫分工原則,這裏其實將分工分爲IO密集型和計算密集型兩種。針對這兩種分工做者提出了不一樣的多線程解決方案。這裏講的多線程解決方案是爲了提供一種方法來提升程序運行的效率(和單線程相比),這一章沒有討論多線程可能產生的問題。這一章提供了這麼兩個案例:第一個是計算某富商的資產淨值,第二個則是統計某個區間內素數的個數,容易理解第一個用於討論IO密集型,剩下的那個用於討論計算密集型。 工具

這一章會有不少結論性的成果,或者說是做者的經驗,這些經驗在其餘任何一本併發編程的書裏都會有,並且每一個人的結論還都不同,因此實際問題實際解決,這裏只作參考。在解決上述兩個問題的時候,咱們應該有如下兩個關鍵的步驟,第一,肯定線程數,第二,肯定任務數量。關於線程數的肯定,這個說法就不少了,書上給出的結論是: 性能

線程數=CPU可用核心數/(1-阻塞係數),其中阻塞係數取01之間。其中CPU的可用核心數很容易拿到: 優化

Runtime.getRuntime().availableProcessors();

計算密集型任務的阻塞係數爲0,而IO密集型任務的阻塞係數接近1。在實際問題中若是須要比較精確的評估阻塞係數,能夠經過性能分析工具對任務進行跟蹤。在第二步,肯定任務數量上,這個也沒有一個明確的指示,有時候並不必定是任務書等於線程數,針對IO密集型任務,因爲每一個任務的代價基本相同,因此每一個任務分配一個線程仍是比較合理的,可是在計算素數的個數任務中,因爲數的特性,判斷素數所用的時間不一樣的數會有不一樣的效率,因此不能很簡單的將數分幾段,而後分配幾個線程去執行。書上還給出了從一個順序執行的程序改寫成併發程序的代碼,採用的是線程池進行處理。固然你們也能夠試着去寫寫用多線程的方式計算一個區間內的素數。很簡單,都是入門級的代碼,儘可能採用concurrent包裏的工具,這些會更簡單。 google

第三章主要講述了三種能夠處理狀態的方法,shared mutabilityisolated mutabilitypure immutability。這裏的狀態值得是併發編程中常常提的是否具備可變性的狀態。咱們知道,一個類要是狀態是不可變的,那天然就不存在同步的問題。可是不多咱們能夠寫出一個不改變狀態的類。因此這一章就是經過這三種方法的介紹來給出解決這一問題的方案。我只提供術語,有興趣的均可以去google裏搜索,都有相應的結論。 spa

相關文章
相關標籤/搜索