Java多線程三(線程安全的集合及java.util.concurrent包的鎖)

1、線程安全的集合 html

        JDK1.5以前,可使用Venctor和Hashtable,也能夠由java.util.Collections來建立線程安全的集合,如:Connections.synchronizedSet(Set<T>); Connections.synchronizedList(List<T>);Connections.synchronizedMap(Map<K, V>)等,其簡單的原理是每一個方法都增長了synchronized來保證線程安全。 java

JDK1.5以後,提供了java.util.concurrent併發包,它提供的新集合類容許經過在語義中的少許更改來得到更高的併發。 算法

  • CopyOnWriteArrayList 其中的set、add、remove等方法,都使用了ReentrantLock的lock()來加鎖,unlock()來解鎖。當增長元素的時候使用Arrays.copyOf()來拷貝副本,在副本上增長元素,而後改變原引用指向副本。
  • CopyOnWriteArraySet 使用了CopyOnWriteArrayList來存儲數據,remove方法調用CopyOnWriteArrayList的remove方法。add方法調用了CopyOnWriteArrayList的addIfAbsent方法,addIfAbsent一樣使用了ReentrantLock的lock()來加鎖,unlock()來解鎖。 
  • ConcurrentHashMap容許多個修改操做併發進行,其關鍵在於使用了鎖分離技術。它使用了多個鎖來控制對hash表的不一樣部分進行的修改。ConcurrentHashMap內部使用段(Segment)來表示這些不一樣的部分,每一個段其實就是一個小的hash table,它們有本身的鎖(由ReentrantLock來實現的)。只要多個修改操做發生在不一樣的段上,它們就能夠併發進行。

2、 java.util.concurrent包的鎖  緩存

鎖定和原子之Lock 安全

    Java 語言內置了鎖定工具 -- synchronized 關鍵字。當線程得到監視器時(內置鎖定),其餘線程若是試圖得到相同鎖定,那麼它們將被阻塞,直到第一個線程釋放該鎖定。同步還確保隨後得到相同鎖定的線程能夠看到以前的線程在具備該鎖定時所修改的變量的值,從而確保若是類正確地同步了共享狀態的訪問權,那麼線程將不會看到變量的"失效"值,這是緩存或編譯器優化的結果。 數據結構

    雖然同步沒有什麼問題,但它有一些限制,在一些高級應用程序中會形成不便。Lock 接口將內置監視器鎖定的鎖定行爲廣泛化,容許多個鎖定實現,同時提供一些內置鎖定缺乏的功能,如計時的等待、可中斷的等待、鎖定輪詢、每一個鎖定有多個條件等待集合以及無阻塞結構的鎖定。 多線程

public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void unlock();
    Condition newCondition();
}

 ReentrantLock 併發

    ReentrantLock 是具備與隱式監視器鎖定(使用 synchronized 方法和語句訪問)相同的基本行爲和語義的 Lock 的實現,但它具備擴展的能力。 工具

    做爲額外收穫,在競爭條件下,ReentrantLock 的實現要比如今的 synchronized 實現更具備可伸縮性。(有可能在 JVM 的未來版本中改進 synchronized 的競爭性能。) 性能

    這意味着當許多線程都競爭相同鎖定時,使用 ReentrantLock 的吞吐量一般要比 synchronized 好。換句話說,當許多線程試圖訪問 ReentrantLock 保護的共享資源時,JVM 將花費較少的時間來調度線程,而用更多個時間執行線程。

    雖然 ReentrantLock 類有許多優勢,可是與同步相比,它有一個主要缺點 -- 它可能忘記釋放鎖定。建議當得到和釋放 ReentrantLock 時使用下列結構:

Lock lock = new ReentrantLock();
//...
lock.lock();
try {
  // perform operations protected by lock
}
catch(Exception ex) {
 // restore invariants
}finally {
  lock.unlock();
}

 Condition

    就像 Lock 接口是同步的具體化,Condition 接口是 Object 中 wait() 和 notify() 方法的具體化。Lock 中的一個方法是 newCondition(),它要求鎖定向該鎖定返回新的 Condition 對象限制。await()、signal() 和 signalAll() 方法相似於 wait()、notify() 和 notifyAll(),但增長了靈活性,每一個 Lock 均可以建立多個條件變量。這簡化了一些併發算法的實現。

ReadWriteLock

    ReentrantLock 實現的鎖定規則很是簡單 -- 每當一個線程具備鎖定時,其餘線程必須等待,直到該鎖定可用。有時,當對數據結構的讀取一般多於修改時,可使用更復雜的稱爲讀寫鎖定的鎖定結構,它容許有多個併發讀者,同時還容許一個寫入者獨佔鎖定。該方法在通常狀況下(只讀)提供了更大的併發性,同時在必要時仍提供獨佔訪問的安全性。ReadWriteLock 接口和 ReentrantReadWriteLock 類提供這種功能 -- 多讀者、單寫入者鎖定規則,能夠用這種功能來保護共享的易變資源。

 3、原子變量

    即便大多數用戶將不多直接使用它們,原子變量類(AtomicInteger、AtomicLong、AtomicReference 等等)也有充分理由是最顯著的新併發類。這些類公開對 JVM 的低級別改進,容許進行具備高度可伸縮性的原子讀-修改-寫操做。大多數現代 CPU 都有原子讀-修改-寫的原語,好比比較並交換(CAS)或加載連接/條件存儲(LL/SC)。原子變量類使用硬件提供的最快的併發結構來實現。

    許多併發算法都是根據對計數器或數據結構的比較並交換操做來定義的。經過暴露高性能的、高度可伸縮的 CAS 操做(以原子變量的形式),用 Java 語言實現高性能、無等待、無鎖定的併發算法已經變得可行。

    幾乎 java.util.concurrent 中的全部類都是在 ReentrantLock 之上構建的,ReentrantLock 則是在原子變量類的基礎上構建的。因此,雖然僅少數併發專家使用原子變量類,但 java.util.concurrent 類的不少可伸縮性改進都是由它們提供的。

    原子變量主要用於爲原子地更新"熱"字段提供有效的、細粒度的方式,"熱"字段是指由多個線程頻繁訪問和更新的字段。另外,原子變量仍是計數器或生成序號的天然機制。

 

 參考博文:

java.util.concurrent介紹 

ConcurrentHashMap之實現細節

相關文章
相關標籤/搜索