天天看點

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

【死磕 Java 并發】系列是 LZ 在 2017 年寫的第一個死磕系列,一直沒有做一個合集,這篇部落格則是将整個系列做一個概覽。

先來一個總覽圖:

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

【高清圖,請關注“Java技術驿站”公衆号,回複:腦圖JUC】

【死磕Java并發】----- 死磕 Java 并發精品合集
【死磕Java并發】—–深入分析synchronized 的實作原理

synchronized 可以保證方法或者代碼塊在運作時,同一時刻隻有一個方法可以進入到臨界區,同時它還可以保證共享變量的記憶體可見性。深入分析 synchronized 的内在實作機制,鎖優化、鎖更新過程。

【死磕Java并發】—–深入分析volatile的實作原理

volatile 可以保證線程可見性且提供了一定的有序性,但是無法保證原子性。在 JVM 底層 volatile 是采用“記憶體屏障”來實作的。這篇博文将帶你分析 volatile 的本質

【死磕Java并發】—–Java記憶體模型之happens-before

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

定義如下:

如果一個操作happens-before另一個操作,那麼第一個操作的執行結果将對第二個操作可見,而且第一個操作的執行順序排在第二個操作之前。

兩個操作之間存在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 的線程将成功擷取該鎖定并傳回。如果目前線程已經擁有該鎖定,此方法将立即傳回。可以使用 <code>isHeldByCurrentThread()</code> 和 <code>getHoldCount()</code> 方法來檢查此情況是否發生。

這篇部落格帶你了解 重入鎖: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規定了如下幾個不變性:

在入隊的最後一個元素的next為null

隊列中所有未删除的節點的item都不能為null且都能從head節點周遊到

對于要删除的節點,不是直接将其設定為null,而是先将其item域設定為null(疊代器會跳過item為null的節點)

允許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有着不同特性:

SynchronousQueue沒有容量。與其他BlockingQueue不同,SynchronousQueue是一個不存儲元素的BlockingQueue。每一個put操作必須要等待一個take操作,否則不能繼續添加元素,反之亦然。

因為沒有容量,是以對應 peek, contains, clear, isEmpty ... 等方法其實是無效的。例如clear是不執行任何操作的,contains始終傳回false,peek始終傳回null。

SynchronousQueue分為公平和非公平,預設情況下采用非公平性通路政策,當然也可以通過構造函數來設定為公平性通路政策(為true即可)。

若使用 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) 順序來啟用那些被安排在同一執行時間的任務。

PS:如果你覺得文章對你有所幫助,别忘了推薦或者分享,因為有你的支援,才是我續寫下篇的動力和源泉!

作者:chenssy。一個專注于【死磕 Java】系列創作的男人

出處:https://www.cnblogs.com/chenssy/p/9555840.html

作者個人網站:https://www.cmsblogs.com/。專注于 Java 優質系列文章分享,提供一站式 Java 學習資料

目前死磕系列包括:

    1. 【死磕 Java 并發】:https://www.cmsblogs.com/category/1391296887813967872(已完成)

    2.【死磕 Spring 之 IOC】:https://www.cmsblogs.com/category/1391374860344758272(已完成)

    3.【死磕 Redis】:https://www.cmsblogs.com/category/1391389927996002304(已完成)

    4.【死磕 Java 基礎】:https://www.cmsblogs.com/category/1411518540095295488

    5.【死磕 NIO】:https://www.cmsblogs.com/article/1435620402348036096

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。

繼續閱讀