【死磕Java併發】----- 死磕 Java 併發精品合集

【死磕 Java 併發】系列是 LZ 在 2017 年寫的第一個死磕系列,一直沒有作一個合集,這篇博客則是將整個系列作一個概覽。node

先來一個總覽圖:算法

chenssy_juc_201712

【高清圖,請關注「Java技術驛站」公衆號,回覆:腦圖JUC】設計模式

【死磕Java併發】—–深刻分析synchronized 的實現原理數組

synchronized 能夠保證方法或者代碼塊在運行時,同一時刻只有一個方法能夠進入到臨界區,同時它還能夠保證共享變量的內存可見性。深刻分析 synchronized 的內在實現機制,鎖優化、鎖升級過程。緩存

【死磕Java併發】—–深刻分析volatile的實現原理安全

volatile 能夠保證線程可見性且提供了必定的有序性,可是沒法保證原子性。在 JVM 底層 volatile 是採用「內存屏障」來實現的。這篇博文將帶你分析 volatile 的本質數據結構

【死磕Java併發】—–Java內存模型之happens-before多線程

happens-before 原則是判斷數據是否存在競爭、線程是否安全的主要依據,保證了多線程環境下的可見性。併發

定義以下:app

  1. 若是一個操做happens-before另外一個操做,那麼第一個操做的執行結果將對第二個操做可見,並且第一個操做的執行順序排在第二個操做以前。
  2. 兩個操做之間存在happens-before關係,並不意味着必定要按照happens-before原則制定的順序來執行。若是重排序以後的執行結果與按照happens-before關係來執行的結果一致,那麼這種重排序並不非法。

【死磕Java併發】—–Java內存模型之重排序

在執行程序時,爲了提供性能,處理器和編譯器經常會對指令進行重排序,可是不能隨意重排序,不是你想怎麼排序就怎麼排序,它須要知足如下兩個條件:

  • 在單線程環境下不能改變程序運行的結果;
  • 存在數據依賴關係的不容許重排序

as-if-serial 語義保證在單線程環境下重排序後的執行結果不會改變。

【死磕Java併發】—–Java內存模型之分析volatile

volatile的內存語義是:

  • 當寫一個 volatile 變量時,JMM 會把該線程對應的本地內存中的共享變量值當即刷新到主內存中。
  • 當讀一個 volatile 變量時,JMM 會把該線程對應的本地內存設置爲無效,直接從主內存中讀取共享變量

老是說 volatile 保證可見性,happens-before 是 JMM 實現可見性的基礎理論,二者會碰撞怎樣的火花?這篇博文給你答案。

【死磕Java併發】—–Java內存模型之從JMM角度分析DCL

DCL,即Double Check Lock,雙重檢查鎖定。是實現單例模式比較好的方式,這篇博客告訴你 DCL 中爲什麼要加 volatile 這個關鍵字。

【死磕Java併發】—–J.U.C之AQS:AQS簡介

AQS,AbstractQueuedSynchronizer,即隊列同步器。它是構建鎖或者其餘同步組件的基礎框架(如ReentrantLock、ReentrantReadWriteLock、Semaphore等),爲 JUC 併發包中的核心基礎組件。

【死磕Java併發】—–J.U.C之AQS:CLH同步隊列

前線程已經等待狀態等信息構形成一個節點(Node)並將其加入到CLH同步隊列,同時會阻塞當前線程,當同步狀態釋放時,會把首節點喚醒(公平鎖),使其再次嘗試獲取同步狀態。

【死磕Java併發】—–J.U.C之AQS:同步狀態的獲取與釋放

AQS的設計模式採用的模板方法模式,子類經過繼承的方式,實現它的抽象方法來管理同步狀態,對於子類而言它並無太多的活要作,AQS提供了大量的模板方法來實現同步,主要是分爲三類:獨佔式獲取和釋放同步狀態、共享式獲取和釋放同步狀態、查詢同步隊列中的等待線程狀況。

【死磕Java併發】—–J.U.C之AQS:阻塞和喚醒線程

當須要阻塞或者喚醒一個線程的時候,AQS 都是使用 LockSupport 這個工具類來完成。

LockSupport是用來建立鎖和其餘同步類的基本線程阻塞原語。

【死磕Java併發】—–J.U.C之重入鎖:ReentrantLock

一個可重入的互斥鎖定 Lock,它具備與使用 synchronized 方法和語句所訪問的隱式監視器鎖定相同的一些基本行爲和語義,但功能更強大。ReentrantLock 將由最近成功得到鎖定,而且尚未釋放該鎖定的線程所擁有。當鎖定沒有被另外一個線程所擁有時,調用 lock 的線程將成功獲取該鎖定並返回。若是當前線程已經擁有該鎖定,此方法將當即返回。可使用 isHeldByCurrentThread()getHoldCount() 方法來檢查此狀況是否發生。

這篇博客帶你理解 重入鎖:ReentrantLock 內在本質。

【死磕Java併發】—–J.U.C之讀寫鎖:ReentrantReadWriteLock

讀寫鎖維護着一對鎖,一個讀鎖和一個寫鎖。經過分離讀鎖和寫鎖,使得併發性比通常的排他鎖有了較大的提高:在同一時間能夠容許多個讀線程同時訪問,可是在寫線程訪問時,全部讀線程和寫線程都會被阻塞。

讀寫鎖的主要特性:

  • 公平性:支持公平性和非公平性。
  • 重入性:支持重入。讀寫鎖最多支持65535個遞歸寫入鎖和65535個遞歸讀取鎖。
  • 鎖降級:遵循獲取寫鎖、獲取讀鎖在釋放寫鎖的次序,寫鎖可以降級成爲讀鎖

【死磕Java併發】—–J.U.C之Condition

在沒有Lock以前,咱們使用synchronized來控制同步,配合Object的wait()、notify()系列方法能夠實現等待/通知模式。在Java SE5後,Java提供了Lock接口,相對於Synchronized而言,Lock提供了條件Condition,對線程的等待、喚醒操做更加詳細和靈活

【死磕Java併發】—-深刻分析CAS

CAS,Compare And Swap,即比較並交換。Doug lea大神在同步組件中大量使用 CAS 技術鬼斧神工地實現了Java 多線程的併發操做。整個 AQS 同步組件、Atomic 原子類操做等等都是以 CAS 實現的。能夠說CAS是整個JUC的基石。

【死磕Java併發】—–J.U.C之併發工具類:CyclicBarrier

CyclicBarrier,一個同步輔助類。它容許一組線程互相等待,直到到達某個公共屏障點 (common barrier point)。在涉及一組固定大小的線程的程序中,這些線程必須不時地互相等待,此時 CyclicBarrier 頗有用。由於該 barrier 在釋放等待線程後能夠重用,因此稱它爲循環 的 barrier。

【死磕Java併發】—–J.U.C之併發工具類:CountDownLatch

CountDownLatch 所描述的是」在完成一組正在其餘線程中執行的操做以前,它容許一個或多個線程一直等待「。

用給定的計數 初始化 CountDownLatch。因爲調用了 countDown() 方法,因此在當前計數到達零以前,await 方法會一直受阻塞。以後,會釋放全部等待的線程,await 的全部後續調用都將當即返回。

【死磕Java併發】—–J.U.C之併發工具類:Semaphore

Semaphore,信號量,是一個控制訪問多個共享資源的計數器。從概念上講,信號量維護了一個許可集。若有必要,在許可可用前會阻塞每個 acquire(),而後再獲取該許可。每一個 release() 添加一個許可,從而可能釋放一個正在阻塞的獲取者。可是,不使用實際的許可對象,Semaphore 只對可用許可的號碼進行計數,並採起相應的行動。

【死磕Java併發】—–J.U.C之併發工具類:Exchanger

能夠在對中對元素進行配對和交換的線程的同步點。每一個線程將條目上的某個方法呈現給 exchange 方法,與夥伴線程進行匹配,而且在返回時接收其夥伴的對象。Exchanger 可能被視爲 SynchronousQueue 的雙向形式。Exchanger 可能在應用程序(好比遺傳算法和管道設計)中頗有用。

【死磕Java併發】—–J.U.C之Java併發容器:ConcurrentHashMap

ConcurrentHashMap 做爲 Concurrent 一族,其有着高效地併發操做。在1.8 版本之前,ConcurrentHashMap 採用分段鎖的概念,使鎖更加細化,可是 1.8 已經改變了這種思路,而是利用 CAS + Synchronized 來保證併發更新的安全,固然底層採用數組+鏈表+紅黑樹的存儲結構。這篇博客帶你完全理解 ConcurrentHashMap。

【死磕Java併發】—–J.U.C之ConcurrentHashMap紅黑樹轉換分析

在 1.8 ConcurrentHashMap 的put操做中,若是發現鏈表結構中的元素超過了TREEIFY_THRESHOLD(默認爲8),則會把鏈表轉換爲紅黑樹,已便於提升查詢效率。那麼具體的轉換過程是怎麼樣的?這篇博客給你答案。

【死磕Java併發】—–J.U.C之Java併發容器:ConcurrentLinkedQueue

ConcurrentLinkedQueue是一個基於連接節點的無邊界的線程安全隊列,它採用FIFO原則對元素進行排序。採用「wait-free」算法(即CAS算法)來實現的。

CoucurrentLinkedQueue規定了以下幾個不變性:

  1. 在入隊的最後一個元素的next爲null
  2. 隊列中全部未刪除的節點的item都不能爲null且都能從head節點遍歷到
  3. 對於要刪除的節點,不是直接將其設置爲null,而是先將其item域設置爲null(迭代器會跳過item爲null的節點)
  4. 容許head和tail更新滯後。這是什麼意思呢?意思就說是head、tail不老是指向第一個元素和最後一個元素(後面闡述)。

【死磕Java併發】—–J.U.C之Java併發容器:ConcurrentSkipListMap

咱們在Java世界裏看到了兩種實現key-value的數據結構:Hash、TreeMap,這兩種數據結構各自都有着優缺點。

  • Hash表:插入、查找最快,爲O(1);如使用鏈表實現則可實現無鎖;數據有序化須要顯式的排序操做。
  • 紅黑樹:插入、查找爲O(logn),但常數項較小;無鎖實現的複雜性很高,通常須要加鎖;數據自然有序。

這裏介紹第三種實現 key-value 的數據結構:SkipList。SkipList 有着不低於紅黑樹的效率,可是其原理和實現的複雜度要比紅黑樹簡單多了。

ConcurrentSkipListMap 其內部採用 SkipLis 數據結構實現。

【死磕Java併發】—–J.U.C之阻塞隊列:ArrayBlockingQueue

ArrayBlockingQueue,一個由數組實現的有界阻塞隊列。該隊列採用FIFO的原則對元素進行排序添加的。

ArrayBlockingQueue 爲有界且固定,其大小在構造時由構造函數來決定,確認以後就不能再改變了。ArrayBlockingQueue 支持對等待的生產者線程和使用者線程進行排序的可選公平策略,可是在默認狀況下不保證線程公平的訪問,在構造時能夠選擇公平策略(fair = true)。公平性一般會下降吞吐量,可是減小了可變性和避免了「不平衡性」。

【死磕Java併發】—–J.U.C之阻塞隊列:PriorityBlockingQueue

PriorityBlockingQueue是一個支持優先級的無界阻塞隊列。默認狀況下元素採用天然順序升序排序,固然咱們也能夠經過構造函數來指定Comparator來對元素進行排序。須要注意的是PriorityBlockingQueue不能保證同優先級元素的順序。

【死磕Java併發】—–J.U.C之阻塞隊列:DelayQueue

DelayQueue是一個支持延時獲取元素的無界阻塞隊列。裏面的元素所有都是「可延期」的元素,列頭的元素是最早「到期」的元素,若是隊列裏面沒有元素到期,是不能從列頭獲取元素的,哪怕有元素也不行。也就是說只有在延遲期到時纔可以從隊列中取元素。

DelayQueue主要用於兩個方面:

  • 緩存:清掉緩存中超時的緩存數據
  • 任務超時處理

【死磕Java併發】—–J.U.C之阻塞隊列:SynchronousQueue

SynchronousQueue與其餘BlockingQueue有着不一樣特性:

  1. SynchronousQueue沒有容量。與其餘BlockingQueue不一樣,SynchronousQueue是一個不存儲元素的BlockingQueue。每個put操做必需要等待一個take操做,不然不能繼續添加元素,反之亦然。
  2. 由於沒有容量,因此對應 peek, contains, clear, isEmpty ... 等方法實際上是無效的。例如clear是不執行任何操做的,contains始終返回false,peek始終返回null。
  3. SynchronousQueue分爲公平和非公平,默認狀況下采用非公平性訪問策略,固然也能夠經過構造函數來設置爲公平性訪問策略(爲true便可)。
  4. 若使用 TransferQueue, 則隊列中永遠會存在一個 dummy node(這點後面詳細闡述)。

SynchronousQueue很是適合作交換工做,生產者的線程和消費者的線程同步以傳遞某些信息、事件或者任務。

【死磕Java併發】—–J.U.C之阻塞隊列:LinkedTransferQueue

LinkedTransferQueue 是基於鏈表的 FIFO 無界阻塞隊列,它出如今 JDK7 中。Doug Lea 大神說 LinkedTransferQueue 是一個聰明的隊列。它是 ConcurrentLinkedQueue、SynchronousQueue (公平模式下)、無界的LinkedBlockingQueues 等的超集。

【死磕Java併發】—–J.U.C之阻塞隊列:LinkedBlockingDeque

LinkedBlockingDeque 是一個由鏈表組成的雙向阻塞隊列,雙向隊列就意味着能夠從對頭、對尾兩端插入和移除元素,一樣意味着 LinkedBlockingDeque 支持 FIFO、FILO 兩種操做方式。

LinkedBlockingDeque 是可選容量的,在初始化時能夠設置容量防止其過分膨脹,若是不設置,默認容量大小爲 Integer.MAX_VALUE。

【死磕Java併發】—–深刻分析ThreadLocal

ThreadLocal 提供了線程局部 (thread-local) 變量。這些變量不一樣於它們的普通對應物,由於訪問某個變量(經過其get 或 set 方法)的每一個線程都有本身的局部變量,它獨立於變量的初始化副本。ThreadLocal實例一般是類中的 private static 字段,它們但願將狀態與某一個線程(例如,用戶 ID 或事務 ID)相關聯。

因此ThreadLocal與線程同步機制不一樣,線程同步機制是多個線程共享同一個變量,而ThreadLocal是爲每個線程建立一個單獨的變量副本,故而每一個線程均可以獨立地改變本身所擁有的變量副本,而不會影響其餘線程所對應的副本。能夠說ThreadLocal爲多線程環境下變量問題提供了另一種解決思路。

【死磕Java併發】—–J.U.C之線程池:ThreadPoolExecutor

鼎鼎大名的線程池。不須要多說!!!!!

這篇博客深刻分析 Java 中線程池的實現。

【死磕Java併發】—–J.U.C之線程池:ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor 是實現線程的週期、延遲調度的。

ScheduledThreadPoolExecutor,繼承 ThreadPoolExecutor 且實現了 ScheduledExecutorService 接口,它就至關於提供了「延遲」和「週期執行」功能的 ThreadPoolExecutor。在JDK API中是這樣定義它的:ThreadPoolExecutor,它可另行安排在給定的延遲後運行命令,或者按期執行命令。須要多個輔助線程時,或者要求 ThreadPoolExecutor 具備額外的靈活性或功能時,此類要優於 Timer。 一旦啓用已延遲的任務就執行它,可是有關什麼時候啓用,啓用後什麼時候執行則沒有任何實時保證。按照提交的先進先出 (FIFO) 順序來啓用那些被安排在同一執行時間的任務。

相關文章
相關標籤/搜索