天天看點

java并發也許你不知道的一些内容(讀Java Concurrency in Practice)(轉)

轉自:http://snake1987.javaeye.com/blog/733843

1.線程安全

a.無狀态的類是線程安全的

b.所有狀态都具有原子性的類是線程安全的

原子性:即對該類的操作是不被打斷的,即使在多線程的環境下

如果一個類隻有單個狀态,推薦使用jdk中的java.util.concurrent.atomic包AtomicBoolean

AtomicInteger

AtomicIntegerArray

AtomicIntegerFieldUpdater

AtomicLong

AtomicLongArray

AtomicLongFieldUpdater

AtomicMarkableReference

AtomicReference

AtomicReferenceArray

AtomicReferenceFieldUpdater

AtomicStampedReference

這些類作為狀态都具有原子性

2.Synchronized

a.如果一個方法用Synchronized關鍵字修飾,則調用該方法所用的鎖,為調用方法的對象的固有鎖(如反射),如果同時帶有Static關鍵字修飾,則使用的鎖為該方法所在的類的固有鎖。

固有鎖:每個對象都隐性地帶有一個鎖,該鎖為重入鎖(重入鎖将在後文介紹),用于synchronized關鍵字

3.重入鎖

關于重入鎖的解釋用書中英文的解釋會更準确:But because intrinsic locks are reentrant, if a thread tries to acquire a lock that it already holds, the request succeeds.

可能看完上面的還是有點難了解

舉一個書中的例子:

看以下代碼

java并發也許你不知道的一些内容(讀Java Concurrency in Practice)(轉)

假設不是可重入鎖,那會出現一個什麼現象?

調用super.doSomething()時發現該方法被鎖住了,然後等待,但等待的鎖自己正持有着,也就變成了死鎖

重入鎖的實作:

重入鎖是通過一個count和記錄owner--一個持有該鎖的Thread來實作的。當count值為0,認為這個鎖目前沒被任何線程持有。當 一個線程持有了該鎖了,jvm就會把owner置為該線程,并把count置為1,如果同樣鎖線程再次請求該鎖,那jvm就會把count+1,如果線程 退出同步塊,則count-1,當count為0時,鎖就釋放了。

4.線程狀态的鎖操作

對于每一個涉及到多個變量的不變量,所有涉及到的變量都應該用同一個鎖來進行同步

怎麼了解呢?

看例子:

java并發也許你不知道的一些内容(讀Java Concurrency in Practice)(轉)

在vector中,每個方法都是用synchronized修飾的,但在上面的使用中--如果兩個線程先後進入該語句塊,再輪流執行 vector.contain(element),顯然還是會添加兩次同樣的element到vector中,是以還是不能保證vector的原子性

5.程式中的代碼是可能被打亂的

如果沒有進行同步,編譯器,程序,和運作環境将有可能會打亂運作的順序。

如文中所舉例子:

java并發也許你不知道的一些内容(讀Java Concurrency in Practice)(轉)

将有可能出現這樣的狀況:輸出結果為0.

這是因為執行的順序被打亂了

6.64位資料的讀或寫是非原子性的

一般的資料類型,如int,執行

int a = 1;

int b = a;

這些操作都是原子操作,但是像long,double就不是了

jvm是允許把64位資料的讀和寫分開兩次操作的,每次操作32位。

是以,有些項目,我們對某些資料的實時性不會太在意,就不會定義該變量為原子變量,但如果是long或者double類型,就要小心了。很有可能某使用者在讀取的時候讀到的是前一個資料的前32位,和後一個資料的後32位

7.volatile關鍵字

當一個變量用volatile聲明了,編譯器會放一個notice到該變量的共享區,然後當執行的時候,就不會對這個變量在程式中的執行順序進行 修改。并且volatile不會緩存在寄存器中,也不會緩存在别的程序中,是以,讀volatile修飾的變量時總能傳回一個最新更新的值。

使用鎖能同時保證變量的可見性和原子性,但volatile隻能保證其可見性。

什麼時候不适合使用volatile?

舉一個經典的例子:i++操作,在并發環境下volatile是不能保證其正确性的

什麼時候可以用volatile?

* 該變量的值不依賴該變量目前的值,像i++,或者你能保證該變量的更新隻在單線程的環境下出現

* 該變量不和其他的變量一起包含在一個不變量中,如上文的vector

* 由于其他的原因,該變量不需要使用鎖