1. 程式人生 > 其它 >保障執行緒安全相關問題

保障執行緒安全相關問題

1.Java 執行時儲存空間

Java執行時(Java runtime)空間可以分為棧區,堆區與方法區(非堆空 間).

棧空間(Stack Space)為執行緒的執行準備一段固定大小的儲存空間, 每個執行緒都有獨立的執行緒棧空間,建立執行緒時就為執行緒分配棧空間.在 執行緒棧中每呼叫一個方法就給方法分配一個棧幀,棧幀用於儲存方法 的區域性變數,返回值等私有資料, 即區域性變數儲存在棧空間中, 基本 型別變數也是儲存在棧空間中, 引用型別變數值也是儲存在棧空間 中,引用 的物件儲存在堆中. 由於執行緒棧是相互獨立的,一個執行緒不 能訪問另外一個執行緒的棧空間,因此執行緒對區域性變數以及只能通過當 前執行緒的區域性變數才能訪問的物件進行的操作具有固定的執行緒安全 性.
堆空間(Heap Space)用於儲存物件,是在 JVM 啟動時分配的一段可以動態擴容的記憶體空間. 建立物件時,在堆空間中給物件分配儲存空 間,例項變數就是儲存在堆空間中的, 堆空間是多個執行緒之間可以共 享的空間,因此例項變數可以被多個執行緒共享. 多個執行緒同時操作實 例變數可能存線上程安全問題
非堆空間(Non-Heap Space)用於儲存常量,類的元資料等, 非堆空 間也是在 JVM 啟動時分配的一段可以動態擴容的儲存空間.類的元數 據包括靜態變數,類有哪些方法及這些方法的元資料(方法名,引數,返 回值等). 非堆空間也是多個 執行緒可以共享的, 因此訪問非堆空間中 的靜態變數也可能存線上程安全問題
堆空間和非堆空間是執行緒可以共享的空間,即例項變數與靜態變 量是執行緒可以共享的,可能存線上程安全問題. 棧空間是執行緒私有的 儲存空間,區域性變數儲存在棧空間中,區域性變數具有固定的執行緒安全性

2.無狀態物件

物件就是資料及對資料操作的封裝, 物件所包含的資料稱為物件 的狀態(State), 例項變數與靜態變數稱為狀態變數.
如果一個類的同一個例項被多個執行緒共享並不會使這些執行緒儲存 共享的狀態,那麼該類的例項就稱為無狀態物件(Stateless Object). 反 之如果一個類的例項被多個執行緒共享會使這些執行緒存在共享狀態,那該類的例項稱為有狀態物件. 實際上無狀態物件就是不包含任何 例項變數也不包含任何靜態變數的物件.
執行緒安全問題的前提是多個執行緒存在共享的資料,實現執行緒安全 的一種辦法就是避免在多個執行緒之間共享資料,使用無狀態物件就是 這種方法

3.不可變物件

不可變物件是指一經建立它的狀態就保持不變的物件,不可變對 象具有固有的執行緒安全性. 當不可變物件現實實體的狀態發生變化 時,系統會建立一個新的不可變物件,就如 String 字串物件. 一個不 可變物件需要滿足以下條件:

  1. 類本身使用 final 修飾,防止通過建立子類來改變它的定義
  2. 所有的欄位都是 final 修飾的,final 欄位在建立物件時必須顯示 初始化,不能被修改
  3. 如果欄位引用了其他狀態可變的物件(集合,陣列),則這些欄位 必須是 private 私有的

不可變物件主要的應用場景:

  1. 被建模物件的狀態變化不頻繁
  2. 同時對一組相關資料進行寫操作,可以應用不可變物件,既可以 保障原子性也可以避免鎖的使用
  3. 使用不可變物件作為安全可靠的Map鍵, HashMap鍵值對的存 儲位置與鍵的 hashCode()有關,如果鍵的內部狀態發生了變化會導致 鍵的雜湊碼不同,可能會影響鍵值對的儲存位置. 如果 HashMap 的鍵 是一個不可變物件,則 hashCode()方法的返回值恆定,儲存位置是固定 的.

4.執行緒特有物件

我們可以選擇不共享非執行緒安全的物件,對於非執行緒安全的物件, 每個執行緒都建立一個該物件的例項,各個執行緒執行緒訪問各自建立的實 例,一個執行緒不能訪問另外一個執行緒建立的例項. 這種各個執行緒建立 各自的例項,一個例項只能被一個執行緒訪問的物件就稱為執行緒特有對 象. 執行緒特有物件既保障了對非執行緒安全物件的訪問的執行緒安全,又 避免了鎖的開銷.執行緒特有物件也具有固有的執行緒安全性.
ThreadLocal類相當於執行緒訪問其特有物件的代理,即各個執行緒 通過 ThreadLocal 物件可以建立並訪問各自的執行緒特有物件,泛型 T 指 定了執行緒特有物件的型別. 一個執行緒可以使用不同的 ThreadLocal 實 例來建立並訪問不同的執行緒特有物件

ThreadLocal 例項為每個訪問它的執行緒都關聯了一個該執行緒特有的 物件, ThreadLocal 例項都有當前執行緒與特有例項之間的一個關聯.

5.裝飾器模式

裝飾器模式可以用來實現執行緒安全,基本思想是為非執行緒安全的 物件建立一個相應的執行緒安全的外包裝物件,客戶端程式碼不直接訪問 非執行緒安全的物件而是訪問它的外包裝物件. 外包裝物件與非執行緒 安全的物件具有相同的介面,即外包裝物件的使用方式與非執行緒安全 物件的使用方式相同,而外包裝物件內部通常會藉助鎖,以執行緒安全的 方式呼叫相應的非執行緒安全物件的方法.
在 java.util.Collections 工具類中提供了一組 synchronizedXXX(xxx) 可以把不是執行緒安全的 xxx 集合轉換為執行緒安全的集合,它就是採用 了這種裝飾器模式. 這個方法返回值就是指定集合的外包裝物件.這 類集合又稱為同步集合.
使用裝飾器模式的一個好處就是實現關注點分離,在這種設計中, 實現同一組功能的物件的兩個版本:非執行緒安全的物件與執行緒安全的 物件. 對於非執行緒安全的在設計時只關注要實現的功能,對於執行緒安 全的版本只關注執行緒安全性