1. 程式人生 > >Java並發編程(三)概念介紹

Java並發編程(三)概念介紹

ava cnblogs 內置 並且 變化 red sha 周期 圖片

在構建穩健的並發程序時,必須正確使用線程和鎖。但是這終歸只是一些機制。要編寫線程安全的代碼,其核心在於要對狀態訪問操作進行管理,特別是對共享的(Shared)可變的(Mutable)狀態的訪問。

對象的狀態是指存儲在狀態變量(例如實例或靜態域)中的數據。

對象的狀態可能包括其他依賴對象的域。比如某個HashMap的狀態不僅是HashMap對象本身,還存儲在許多Map.Entry對象中。

"共享"意味著變量可以由多個線程同時訪問,而"可變"則意味著變量的值在其生命周期內可以變化。

技術分享圖片

線程安全性在於如何防止數據上發生不可控的並發訪問

一個對象是否需要是線程安全的,取決於它是否被多個線程訪問。

要使對象是線程安全的,需要采用同步機制

來協同對對象可變狀態的訪問。如果無法實現協同,那麽可能導致數據破壞以及其他不該出現的結果。

協同多個線程對變量訪問的同步機制主要有:

1. 關鍵字synchronized

2. 關鍵字volatile

3. 顯式鎖(Explicit Lock)

4. 原子變量

協同多線程訪問一個可變的狀態變量的方法有:

1. 不在線程之間共享該狀態變量

2. 將狀態變量修改為不可變的變量

3. 在訪問狀態變量時使用同步

什麽是線程安全?

一個類在多線程環境下被訪問,這個類始終能表現出正確的行為,那麽就稱這個類是線程安全的。

在線程安全類中往往都封裝了必要的同步機制,因此客戶端無須進一步采取措施。

無狀態的對象一定是線程安全的。

無狀態可以極大降低類在實現線程安全性時的復雜性。只有當類在保存一些信息的時候,線程安全才會成為一個問題。

原子性

當一個操作被CPU無可分割地執行時就是原子操作。

典型的非原子性操作就是a++,但是實際上這是一個"讀取—修改—寫入"的操作序列,並且結果狀態依賴於之前的狀態。

在並發編程中,由於不恰當的執行順序而出現不正確的結果是一種非常重要的情況,就是競態條件(Race Condition)。

競態條件

當某個計算的正確性取決於多個線程的交替執行時序時,那麽就會發生競態條件。最常見的競態條件就是"先檢查後執行(Check-Then-Act)"操作,即通過一個可能失效的觀測結果來決定下一步的動作。比如延遲初始化的單例模式。

復合操作

要避免競態條件的問題,就必須在某個線程修改該變量時,通過某種方式防止其他線程使用這個變量,從而確保其他線程只能在修改操作完成或者之後讀取和修改狀態,而不是在修改狀態的過程中。為了確保安全性,"先檢查後執行"(例如延遲初始化)和"讀取—修改—寫入"(如遞增運算)等必須是原子的。我們把"先檢查後執行"(例如延遲初始化)和"讀取—修改—寫入"等操作統稱為復合操作:包含了一組必須以原子方式執行的操作以確保線程安全性。

復合操作

加鎖機制

內置鎖

同步代碼塊

重入

Java並發編程(三)概念介紹