最近在閱讀 《Inside theJVM》 這本書,結合一些平常工做學習中的感想,隨便寫一些東西,走馬觀花,沒必要有章法。
關於「單例同步」:java
一直有人在問單例對象的併發調用是否須要同步,基本屬於「月經帖」了,答案是現成的滿天下都是,但真正能讓人內心踏實下來的解釋寥寥無幾。實際上,只要學習了一些JVM的運行原理,解釋這個問題就不難了。多線程
若是一個類是單例的,好比某些DAO的設計,那麼全部的線程來訪問這個類的實例的時候,它們得到的都將是同一個對象,這是不言自明的。若是這些線程的當前
操做是「互斥」的,那麼每一個線程就必須在取得該實例的訪問資格的時候爲該對象上鎖,以獨享該對象直到當前操做結束,以避免在操做中途被其它線程介入而產生不
可預知的結果。問題是,什麼樣的操做是「互斥」的呢?併發
簡單地說,互斥操做就是兩個操做企圖對它倆共享的某個資源進行修改,而修改的結果是不可預知的。因而問題就變成了,什麼纔是「共享的資源」?從純粹
Java語法的角度這個問題無法解釋,由於它遵循的是當前java虛擬機的規範描述。現假設兩個線程正企圖同時訪問一個單例對象的方法,如,ide
int method1(int i) { int j = 3; return i+j; }
一個規範的虛擬機線程在調用method1()的時候是這樣作的:學習
1) 把method1()的局部變量,包括參數,壓入當前線程的棧; 2) 從當前線程棧彈出變量j,並賦予數值3; 3) 從當前線程棧彈出參數i,與j執行加法運算; 4) 從當前線程棧中釋放當前方法佔用的棧幀,並把method1()的結果壓入當前線程棧。
須要說明的是,當前線程棧是當前線程獨有的,絕對不會被其它線程訪問到。這樣,只要你在method1()裏面使用的全都是局部變量或參數,那就不須要爲多線程的併發調用發愁,由於每一個線程都有本身的棧幀,各不相干。
複雜一點,若是method1()是這樣定義的:spa
int method1(int i, SingletonClass singleObj) { singleObj.intValue ++; int j = i + singleObj.intValue; return j; }
這下咱們就不得不考慮線程同步問題了,這個方法顯然包含了一個互斥的操做「singleObj.intValue ++;」。
前面說過,方法的參數會被壓入當前線程私有的棧直到方法結束,但這裏要注意的是,singleObj只是一個引用地址而非真正的對象實例,所以,儘管
singleObj這個引用值是被壓入線程私有棧去的,但真正的對象實例倒是在堆裏存放的,棧雖然是線程私有的,堆倒是全部線程共享的,所以
singleObj的成員變量intValue是徹底有可能在當前線程執行第二行代碼前被其它線程修改了的。好比說,線程1調用mothod1()的時候
singleObj.intValue的值是1,
i的值是2,那麼正確的狀況下,method1()的返回值應該是4。但當線程1和線程2幾乎同時調用method1(),線程2剛好在線程1把
intValue變成2以後的一瞬間又執行了一次singleObj.intValue
++,因爲singleObj是單例,兩個線程遇到的singleObj是同一個對象,所以此次運算將把intValue變成3。接下來線程1繼續第二行
代碼,結果j的結果變成了i+3 = 2+3 = 5 。 如此一來,線程1調用method1()的返回結果究竟會是 4 仍是 5
是沒法肯定的,只能憑運氣,寄望線程2在線程1從調用method1()到取得返回值之間的這段時間打盹。在絕大多數狀況下,這種「憑運氣」的作法是不能
接受的,咱們須要向線程1保證,在它調用method1()期間毫不會收到線程2的干擾。作法以下:.net
int method1(int i, SingletonClass singleObj) { int j = 0; synchronize(singleObj) { singleObj.intValue ++; j = i + singleObj.intValue; } return j; }
這個寫法仍然有缺陷,由於線程2極可能在線程1執行int j = 0 的時候修改singleObj的intValue,因此比較可靠的應該在調用method1()以前鎖住singleObj:線程
synchronize(singleObj) { int result = obj.method1(2, singleObj); }
小小總結一下,「一個方法若是涉及對某個共享對象(或堆對象)的寫操做,那麼它必須同步該對象」這個說法在大多數狀況下都對,但還有些失之籠統,或許這樣說比較準確些,「若是一個方法對某共享對象的寫操做會形成其它線程返回值的不肯定性,則該方法應該同步該對象。」
更正
:本文出現的書名應該是《Inside the JVM》。本書是Java世界的經典著做,有興趣的網友能夠用書名在網上找到一大堆資料,其中文譯名是《深刻Java虛擬機》。設計
原文出處:http://blog.csdn.net/leeshaoqun/article/details/4716385code