Java 併發編程 - Programming Concurrency on the JVM

這幾個月一直在做性能調優的工作,以前總是進行功能的開發,從來不考慮性能的問題,經過這幾個月的工作,發現從性能和擴展性的角度去看軟件開發,還真是大不一樣。在和朋友聊天的時候,提及Java程序是否能充分利用多核cpu的問題的時候,朋友給我推薦了這本書《Programming Concurrency on the JVM》。幾天看下來,還真覺得很應景,建議做Java開發的朋友試着閱讀一下。我簡單記錄下我的讀後感。

從多線程角度重新檢查你的程序

一直習慣於在JavaEE的開源框架下做開發,認爲多線程是容器(server)和框架的事情。其實不是的。我們定義的每一個類,如果是在多線程環境下被使用,你就得考慮線程安全和高併發性。

線程安全(thread-safe)VS高併發

所謂線程安全,就是指當同一個對象的狀態(屬性)被多個線程同時讀寫的時候,會不會產生衝突和不一致的問題。傳統的JDK給我們提供了同步 (synchronize)來避免這個問題,但這往往會造成性能的瓶頸。從JDK5開始,JDK提供了concurrent包,可以幫助我們在很多情況下 避免使用同步來解決線程安全的問題。

設計不變類(immutable), 分離可變類(mutable)

其實,優良的設計是可以避免很多的線程安全問題,並提供高併發和高可用性的支持。最重要的一個設計方法就是設計不變類(immutable)。如果你的類的實例在創建之後就不再能被改變,那麼你就不用擔心讀寫衝突,也就是線程安全了,這樣你就可以自由的cache和共享這個類的實例了。Hibernate的SessionFactory就是這樣設計的,所以SessionFactory是線程安全的。另外JDK的String還有Integer等wrapper類也都是不變類。

當然我們不能避免使用會發生狀態變化的類,只是我們要儘量把可變的類和不可變的類分離出來(這其實也是OOD的一個原則)。

使用concurrent包

對於可變的類,也儘量不要使用synchronize。可以使用concurrent提供的lock,這個包提供了讀寫lock,比synchronize力度更細。其實有點類似於數據庫的鎖的設計了。

使用Akka - software transactional design (STD)

Akka是一個實現了(STD)的框架,對Java和Scala都有很好的支持。什麼是STD呢?其實是藉助事務設計的理念來處理併發問題。其實這個也是借鑑了DB的事務設計理念。試想一下,數據庫作爲一個共享並且可變的資源,能在多線程下工作的那麼好,無非藉助於優良的鎖和事務的機制。STD借鑑了樂觀鎖的事務機制。STD假設你的共享數據被頻繁的讀和寫,但是同時寫的可能性比較小。試想一下,很多時候我們的共享數據都是用戶相關的,也就是不同的用戶有着不同的可變狀態類,只要你保住同一個用戶在操作的時候調用的服務不要有併發的問題,也就不會衝突,這大概也是爲什麼我們一直不怎麼注意線程安全同時又沒有遇到什麼問題的原因。對於一個系統管理的信息,比如幾個管理員有可能同時改變某個系統設置,有可能產生併發訪問。這種情況很少,一方面因爲管理員用戶本來就很少,他們同時操作同一個數據的可能性就很少了。而且現在的權限設計很細緻,以至於不同的管理員也有不同的管理數據域。但是,我們也不能完全避免併發性,雖然概率比較小。這個時候STD就發揮作用了,它確保在寫的時候,如果沒有被別的線程捷足先登,它就寫進去,萬一有別的線程在它讀之後,寫之前修改了這個數據,它就回滾整個事務,並且retry。這就是樂觀鎖的思想。

從上面的分析可以看出,在頻繁讀寫同時寫衝突很少發生的情況下,可以使用STD取得較好的高併發性。

其實STD,也給我們另一個啓事,那就是我們在設計代碼的時候,是不是可以加入事務的考慮?那樣我們的程序更爲安全和合理,Akka提供了這方面的API,很有意思。

使用Akka - Actors

我還沒有看到,看完再續

 

待續...

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章