當前位置:
首頁 > 知識 > 面試必問:讀寫一致性,你需要思考的問題

面試必問:讀寫一致性,你需要思考的問題

先說明下,本文要討論的多線程讀寫是指一個線程寫,一個或多個線程讀,不包括多線程同時寫的情況。

試想下這樣一個場景:一個線程往hashmap中寫數據,一個線程往hashmap中讀數據。 這樣會有問題嗎?如果有,那是什麼問題?

相信大家都知道是有問題的,但至於到底是什麼問題,可能就不是那麼顯而易見了。

問題有兩點。

一是內存可見性的問題,hashmap存儲數據的table並沒有用voliate修飾,也就是說讀線程可能一直讀不到數據的最新值。

二是指令重排序的問題,get的時候可能得到的是一個中間狀態的數據,我們看下put方法的部分代碼。

final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {

... if ((p = tab[i = (n - 1) hash]) == null)

tab[i] = new Node(hash, key, value, next);

...

}

可以看到,在put操作時,如果table數組的指定位置為null,會創建一個Node對象,並放到table數組上。但我們知道jvm中 tab[i] = new Node(hash, key, value, next);這樣的操作不是原子的,並且可能因為指令重排序,導致另一個線程調用get取tab[i]的時候,拿到的是一個還沒有調用完構造方法的對象,導致不可預料的問題發生。

上述的兩個問題可以說都是因為HashMap中的內部屬性沒有被voliate修飾導致的,如果HashMap中的對象全部由voliate修飾,則一個線程寫,一個線程讀的情況是不會有問題(這裡是我的猜測,證實這個猜測正確性的一點依據是ConcurrentHashMap的get並沒有加鎖,也就是說在Map結構里讀寫其實是不衝突)。見下方區sora-zero同學的評論

創建對象的原子性問題

有的同學對於 Object obj = new Object();這樣的操作在多線程的情況下會拿到一個未初始化的對象這點可能有疑惑,這裡也做個簡單的說明。以上java語句分為4個步驟:

在棧中分配一片空間給obj引用

在jvm堆中創建一個Object對象,注意這裡僅僅是分配空間,沒有調用構造方法

初始化第2步創建的對象,也就是調用其構造方法

棧中的obj指向堆中的對象

以上步驟看起來也是沒有問題的,畢竟創建的對象要調用完構造方法後才會被引用。

但問題是jvm是會對指令進行重排序的,重排之後可能是第4步先於第3步執行,那這時候另外一個線程讀到的就是沒有還執行構造方法的對象,導致未知問題。jvm重排只保證重排前和重排後在單線程中的結果一致性。

注意java中引用的賦值操作一定是原子的,比如說a和b均是對象的情況下不管是32位還是64位jvm,a=b操作均是原子的。但如果a和b是long或者double原子型數據,那在32位jvm上a=b不一定是原子的(看jvm具體實現),有可能是分成了兩個32位操作。 但是對於voliate的long,double 變數來說,其賦值是原子的。

具體可以看這裡https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.7

資料庫中讀寫一致性

跳出hashmap,在資料庫中都是要用mvcc機制避免加讀寫鎖。也就是說如果不用mvcc,資料庫是要加讀寫鎖的,那為什麼資料庫要加讀寫鎖呢?原因是寫操作不是原子的,如果不加讀寫鎖或mvcc,可能會讀到中間狀態的數據,以HBase為例,Hbase寫流程分為以下幾個步驟:

1.獲得行鎖

2.開啟mvcc

3.寫到內存buffer

4.寫到append log

5.釋放行鎖

6.flush log

7.mvcc結束(這時才對讀可見)

試想,如果沒有不走 2,7 也不加讀寫鎖,那在步驟3的時候,其他的線程就能讀到該數據。如果說3之後出現了問題,那該條數據其實是寫失敗的。也就是說其他線程曾經讀到過不存在的數據。

同理,在mysql中,如果不用mvcc也不用讀寫鎖,一個事務還沒commit,其中的數據就能被讀到,如果用讀寫鎖,一個事務會對中更改的數據加寫鎖,這時其他讀操作會阻塞,直到事務提交,對於性能有很大的影響,所以大多數情況下資料庫都採用MVCC機制實現非鎖定讀。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 千鋒JAVA開發學院 的精彩文章:

監控信息推與拉
你所不知道的HelloWorld背後的原理

TAG:千鋒JAVA開發學院 |