Java中primitive type的線程安全性

Java中primite type,如char,integer,bool之類的,它們的讀寫操做都是atomic的,可是有幾個例外:java

  1. long和double類型不是atomic的,由於long和double都是8字節的,而在32位的CPU上,其機器字長爲32位,操做8個字節須要多個指令操做。
  2. ++i或者i++,由於要先讀後寫,也是多步操做。

這些狀況下,須要使用AutomicInteger,AutomicLong。安全

 

同時,java中的reference的讀寫也是automic的,雖然reference到底佔幾個字節沒有明肯定義,但至少如下能夠保證:app

  1. 在32位的CPU下,使用的是4字節的reference
  2. 在64位的CPU下,可使用4字節或者8字節的reference

因此不管如何,對reference的操做確定是單步操做,是automic的。jvm

事實上,一個蠻有用的例子是cache switch - 你有兩個cache pool,分別用兩個reference指向:cache_in_build和cache_in_use,等cache_in_build完成了,你須要把cache_in_use指向它,沒有問題,這是線程安全的。優化

可是問題是,jvm能夠會reorder你的statement:ui

  1. cache_in_build.build()
  2. cache_in_use = cache_in_build

指望是cache在build完了以後,切換過去。atom

可是這兩行代碼並無先後的dependency關係,因此JVM能夠爲了優化,進行亂序執行,變成:線程

  1. cache_in_use = cache_in_build
  2. cache_in_build.build()

因而,若是另外一個線程在使用cache_in_use,在代碼#1執行完以後,就訪問到了正在build的那個cache,這是個問題。it

解決方案是cache_in_use必須定義爲volatile,保證happen-before的關係,拒絕亂序執行,從而保證其餘線程看到的數據是有效的。im

相關文章
相關標籤/搜索