「Java併發編程實戰」之對象的組合

前言

本系列博客是對讀《Java併發編程實戰》的一點總結,讀這本書感受實在是太枯燥無味了,因此打算讀完總結回顧下,對於想要快速瞭解本書內容的朋友,很是適合閱讀此係列博客。編程

設計線程安全的類

這一小節主要闡述,如何才能設計一個線程安全的類,在設計線程安全類的過程當中,須要包含如下三個要素:數組

  • 找出構成對象狀態的全部變量。
  • 找出約束狀態變量的不變性條件。
  • 創建對象狀態的併發訪問管理策略。
  1. 要建立一個線程安全的類,首先要明白哪些狀態是須要同步的,就是要先收集同步需求。例如:當類中的一個變量的int值是10,每次加一,那麼下一次只能是11。當下一個狀態須要依賴當前狀態時,這個操做必須是一個複合操做。該操做必須是一個原子操做。
  2. 類的不變性條件與後驗條件約束了在對象上有哪些狀態和狀態轉換是有效的,在某些對象的方法中還包含一些基於狀態的先驗條件,例如:不能從空隊列中移除一個元素,在刪除元素前,隊列必須爲非空狀態。若是某個類的操做中包含基於狀態的先驗條件,那麼這個操做就稱爲依賴狀態的操做,能夠經過現有庫中的類(例如阻塞隊列Blocking Queue或信號量)來實現依賴狀態的行爲。
  3. 在Java中也存在全部權模型,只不過垃圾回收器爲咱們減小了許多在引用共享方面常見的錯誤,所以下降了在全部權處理上的開銷,全部權與封裝老是相互關聯的,對象封裝它擁有的狀態,反之對封裝的狀態擁有全部權,狀態變量的全部這將決定採用何種加鎖協議來維持變量狀態的完整性,全部權意味着控制權。這是一個再類設計中須要考慮的一個要素。

對象的狀態:對象的狀態由變量構成,就是類中的字段。安全

不變性條件:不變性條件是,在程序執行過程或部分過程當中,可始終被假定成立的條件。好比,循環的不變性條件就是隻有爲真纔開始循環。bash

實例封閉

在上一節中咱們說過線程封閉,線程封閉主要是用來將數據封裝到線程內部,只能它本身使用。那麼實例封閉是幹什麼的? 若是某個對象不是線程安全的,能夠經過多種技術使其在多線程程序中安全的使用。在此節中介紹了一個Java監視器模式,遵循Java監視器模式的對象會把對象的全部可變狀態都封裝起來,並由對象本身內置的鎖來保護。多線程

//僞代碼
public final class Counter{
    private long value = 0;
    
    public synchronized long getValue(){
        return value;
    }
    public synchronized long increment(){
        return ++value;
    }
}
複製代碼

Java中許多類都使用了Java監視器模式,,例如Vector和Hashtable,Java監視器模式僅僅是一種編寫代碼的約定。併發

線程安全性的委託

  1. 當將線程的安全性委託給多個狀態變量,只要這些變量時彼此獨立的,即組合而成的類並不會在其包含的多個狀態變量上增長任何不變性條件。若是每一個變量都是線程安全的,那麼這個類就是線程安全的,它能夠將本身的線程安全性委託給它本身的變量,前提是這些變量相互直接都彼此獨立,而且是線程安全的。
  2. 大多數組合對象他們的狀態變量之間都存在這某些不變性條件,當狀態變量之間有關聯就會出現複合操做,那麼僅依靠委託並不足以實現線程安全性,在這種狀況下必須提供本身的加鎖機制來保證複合操做都是原子性的。
  3. 若是一個狀態變量是線程安全的,而且沒有任何不變性條件來約束它的值,在變量上的操做也不存在任何不容許的狀態轉換,那麼就能夠安全地發佈這個變量了。

現有線程安全類中添加功能

有時候,某個現有的線程安全類能支持咱們所須要的全部操做,但更多的時候,現有的類只能支持大部分操做,這個時候就須要在不破壞線程安全性的狀況下添加一個新的操做。post

  1. 客戶端加鎖機制 客戶端加鎖是指,對於使用某個對像X的客戶端代碼,使用X自己用於保護其狀態的鎖來保護這段客戶代碼,要使用客戶端加鎖,你必須知道對象X使用的是哪個鎖。客戶端加鎖是脆弱的,由於它將類加鎖的代碼分佈到多個類中。就是使用方必須加鎖。
  2. 組合
//僞代碼
public class ImprovedList<T> implements List {
    private final List<T> list;

    public ImprovedList(List<T> list) {
        this.list = list;
    }

    public synchronized boolean putIfAbsent(T x) {
        boolean contains = list.contains(x);
        if (!contains) {
            list.add(x);
        }
    }

    //按照此種方式委託list的其餘方法
    public synchronized void clear() {
        list.clear();
    }
}
複製代碼

代碼中ImprovedList經過對List對象的操做委託給底層的List實例來實現List的操做,新增一個若是沒有則添加的方法。這就是組合。事實上使用了Java監視器模式來封裝現有的List,而且只要在類中擁有指向底層List的惟一外部引用,就能確保線程安全性。ui

你們看后辛苦點個贊點個關注哦!查看我的主頁,有更多的博客哦。若有錯誤,煩請指正。 有興趣加羣一塊兒交流。 this

相關文章
相關標籤/搜索