java併發編程實踐學習(1)線程安全

編寫線程安全的代碼就是管理對線程中的數據
共享是指一個變量可以被多個線程訪問
可變是指變量的值可以在其生命週期內改變


一.線程安全性**

線程安全是指多個線程在訪問一個類時,如果不需要額外的同步,這個類的行爲仍然是正確的。

1 無狀態的對象永遠是線程安全的

@ThreadSafe
public class StatelessFactorizer implements servlet{
    public void service (ServletRequest req,ServletResponse res){
    BigInteger i = extractFromrequest(req);
    BigInteger[] factors = factor(i);
    encodeIntoResponse(res,factors);
    }
}

它像大多數的servlet一樣沒有狀態,它不包含域對象也沒有引用其他的域對象。

2 原子性

原子性是指單獨的,不可分割的操作。
原子操作是線程安全的。
介紹原子變量前首先介紹原子操作,假設有操作A和B,如果從執行A的角度看,當其他線程執行B時,要麼全部執行完成,要麼一點都沒有執行,這樣A和B互爲原子操作。
原子操作是線程安全的。
例如,”讀-改-寫”操作是一個整體,我們需要它們作爲一個整體執行,而在多線程的環境中它們可能被分割開來,

2.1 競爭條件

指多個線程或者進程在讀寫一個共享數據時結果依賴於它們指令執行的相對時序,即要想得到正確的結果,要依賴於幸運的時序。
最常見的競爭條件是“檢查再運行”,使用一個潛在的過期值作爲決定下一步操作的依據。

2.2 複合操作

不滿足原子操作要求的操作被稱爲複合操作
JDK的Java.util.concurrent.atomic包中包括了原子變量類,這些類用來實現數字和對象引用的原子狀態轉換。z
爲了保證線程安全,操作必須原子的執行,所以就引出了java內置的原子性機制—

2.3 鎖

原子變量自身是線程安全的,但是如果一個不變約束涉及多個變量時,變量間不是彼此獨立的,無論這些變量是否是原子變量都不能確保線程安全,而需要在同一個原子操作中更新這些相互關聯的狀態變量才能確保線程安全。

2.3.1 內部鎖

java提供了強制原子性的內置鎖機制:synchronized塊。
一個synchronized塊有倆部分:鎖對象的引用和這個鎖 保護的代碼塊。
synchronized方法的鎖是該方法所在的對象本身,靜態的synchronized方法的鎖是從Class對象上獲取的鎖。
執行線程在靜茹synchronized塊之前自動獲得鎖,放棄對synchronized塊事自動釋放鎖。
內部鎖在java中扮演了互斥鎖的角色,即至多隻有一個線程可以擁有鎖,沒有獲取到鎖的線程只能等待或阻塞直到鎖被釋放,因此同步塊可以線程安全

2.3.2 重進入

內部鎖是可重進入的
可重入是指對於同一個線程,它可以重新獲得已有它佔用的鎖。
可重入性意味着鎖的請求是基於”每線程”而不是基於”每調用”,它是通過爲鎖關聯一個請求計數器和一個佔有它的線程來實現。當未被佔有時,計數器的值爲零,認爲未被佔有。當 線程請求一個未被佔有的鎖時,jvm將記錄鎖的擁有者並將計數器置爲1。如果同一個線程再次請求這個鎖,計數器遞增,每次退出同步塊計數器遞減,直到計數器爲零鎖被釋放。

2.4 用鎖來保護狀態

僅用synchronized塊包裝複合類型操作是不夠的,當用同步來訪問變量時每次訪問都需要用同一個鎖。
並不是所有的變量都需要鎖保護, 只有那些被多個線程訪問的可變數據需要上鎖。
但是過多的同步會導致活躍度或者性能的問題。

2.5 活躍度與性能

如果同步快過大會導致程序的併發度下降導致性能的下降,所以那些不跨線程共享的代碼和變量就不需要同步,但是如果同步快劃分的過於瑣碎也不合理,因爲上鎖和釋放也會佔用一部分資源。
有些耗時的操作比如:網絡或控制檯的I/O,難以快速完成執行這些操作期間不要佔有鎖。

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