併發編程 知識點

1. Executor框架

2. Fork/join

Fork/Join框架是Java7提供了的一個用於並行執行任務的框架, 是一個把大任務分割成若干個小任務,最終彙總每一個小任務結果後獲得大任務結果的框架。java

咱們再經過Fork和Join這兩個單詞來理解下Fork/Join框架,Fork就是把一個大任務切分爲若干子任務並行的執行,Join就是合併這些子任務的執行結果,最後獲得這個大任務的結果。好比計算1+2+。。+10000,能夠分割成10個子任務,每一個子任務分別對1000個數進行求和,最終彙總這10個子任務的結果。git

重作一道Java面試題(Fork/Join)github

3. happen-before

http://ifeve.com/easy-happens-before/面試

3.1 happens-before偏序關係

synchronized、大部分鎖,衆所周知的一個功能就是使多個線程互斥/串行的(共享鎖容許多個線程同時訪問,如讀鎖)訪問臨界區,但他們的第二個功能 —— 保證變量的可見性 —— 常被遺忘。編程

爲何存在可見性問題?

簡單介紹下。相對於內存,CPU的速度是極高的,若是CPU須要存取數據時都直接與內存打交道,在存取過程當中,CPU將一直空閒,這是一種極大的浪費,媽媽說,浪費是很差的,因此,現代的CPU裏都有不少寄存器,多級cache,他們比內存的存取速度高多了。某個線程執行時,內存中的一份數據,會存在於該線程的工做存儲中(working memory,是cache和寄存器的一個抽象,這個解釋源於《Concurrent Programming in Java: Design Principles and Patterns, Second Edition》§2.2.7,原文:Every thread is defined to have a working memory (an abstraction of caches and registers) in which to store values. 有很多人以爲working memory是內存的某個部分,這多是有些譯做將working memory譯爲工做內存的緣故,爲避免混淆,這裏稱其爲工做存儲,每一個線程都有本身的工做存儲),並在某個特定時候回寫到內存。單線程時,這沒有問題,若是是多線程要同時訪問同一個變量呢?內存中一個變量會存在於多個工做存儲中,線程1修改了變量a的值何時對線程2可見?此外,編譯器或運行時爲了效率能夠在容許的時候對指令進行重排序,重排序後的執行順序就與代碼不一致了,這樣線程2讀取某個變量的時候線程1可能尚未進行寫入操做呢,雖然代碼順序上寫操做是在前面的。這就是可見性問題的由來。數組

咱們沒法枚舉全部的場景來規定某個線程修改的變量什麼時候對另外一個線程可見。但能夠制定一些通用的規則,這就是happens-before。它是一個偏序關係,Java內存模型中定義了許多Action,有些Action之間存在happens-before關係(並非全部Action兩兩之間都有happens-before關係)。「ActionA happens-before ActionB」這樣的描述很擾亂視線,是否是?OK,換個描述,若是ActionA happens-before ActionB,咱們能夠記做hb(ActionA,ActionB)或者記做ActionA < ActionB,這貨在這裏已經不是小於號了,它是偏序關係,是否是隱約有些離散數學的味道,不喜歡?嗯,我也不喜歡,so,下面都用hb(ActionA,ActionB)這種方式來表述。安全

從Java內存模型中取兩條happens-before關係來瞅瞅:數據結構

An unlock on a monitor happens-before every subsequent lock on that monitor.
A write to a volatile field happens-before every subsequent read of that volatile.

對一個monitor的解鎖操做happens-before後續對同一個monitor的加鎖操做」、「對某個volatile字段的寫操做happens-before後續對同一個volatile字段的讀操做」……莫名其妙、不知所云、不能理解……就是這個心情。是否是說解鎖操做要先於鎖定操做發生?這有違常規啊。確實不是這麼理解的。happens-before規則不是描述實際操做的前後順序,它是用來描述可見性的一種規則,下面我給上述兩條規則換個說法:多線程

若是線程1解鎖了monitor a,接着線程2鎖定了a,那麼,線程1解鎖a以前的寫操做都對線程2可見(線程1和線程2能夠是同一個線程)。 若是線程1寫入了volatile變量v(這裏和後續的「變量」都指的是對象的字段、類字段和數組元素),接着線程2讀取了v,那麼,線程1寫入v及以前的寫操做都對線程2可見(線程1和線程2能夠是同一個線程)。併發

是否是很簡單,瞬間以爲這篇文章弱爆了,說了那麼多,其實就是在說「若是hb(a,b),那麼a及以前的寫操做在另外一個線程t1進行了b操做時都對t1可見(同一個線程就不會有可見性問題,下面再也不重複了)」。雖然弱爆了,但還得善始善終,是否是,繼續來,再看兩條happens-before規則:

All actions in a thread happen-before any other thread successfully returns from a join() on that thread.
Each action in a thread happens-before every subsequent action in that thread.

通俗版:

線程t1寫入的全部變量(全部action都與那個join有hb關係,固然也包括線程t1終止前的最後一個action了,最後一個action及以前的全部寫入操做,因此是全部變量),在任意其它線程t2調用t1.join()成功返回後,都對t2可見。 線程中上一個動做及以前的全部寫操做在該線程執行下一個動做時對該線程可見(也就是說,同一個線程中前面的全部寫操做對後面的操做可見) 大體都是這個樣子的解釋。

happens-before關係有個很重要的性質,就是傳遞性,即,若是hb(a,b),hb(b,c),則有hb(a,c)。

Java內存模型中只是列出了幾種比較基本的hb規則,在Java語言層面,又衍生了許多其餘happens-before規則,如ReentrantLock的unlock與lock操做,又如AbstractQueuedSynchronizer的release與acquire,setState與getState等等。

4. 數據結構

 

4.1 ConcurrentHashMap


4.2 ConcurrentLinkedQueue

 

5. 線程池

 

5.1 參數設置


5.2 原理


5.3 拒絕策略

 

6. 線程狀態

 

7. Lock/synchronized

 

8. 原子操做類

 

- java.util.concurrent.atomic

原子操做類至關於泛化的volatile變量,可以支持原子讀取-修改-寫操做。

好比AtomicInteger表示一個int類型的數值,提供了get和set方法,

這些volatile類型的變量在讀取與寫入上有着相同的內存語義。

原子操做類共有13個類,在Java.util.concurrent.atomic包下,能夠分爲四種類型的原子更新類:

原子更新基本類型、原子更新數組類型、原子更新引用和原子更新屬性。

8.1 原子更新基本類型類(只有3個)

AtomicInteger

AtomicBoolean

AtomicLong

核心實現是使用了unsafe的循環CAS。

unsafe也只提供了3種CAS:compareAndSwapObject,compareAndSwapInt,compareAndSwapLong

由於原子操做類只支持3種,其餘的基本類型可使用這三個代替。

8.2 原子更新數組

AtomicIntegerArray

AtomicLongArray

AtomicReferenceArray

構造方法傳入的是個數組,原子類會複製一個數組。

8.3 原子更新引用類型

AtomicReference

AtomicReferenceFieldUpdater

AtomicMarkableReference

8.4 原子更新字段類

AtomicIntegerFieldUpdater

AtomicLongFieldUpdater

AtomicStampedReference 能夠解決CAS的ABA問題

9. 併發工具類

9.1 CountDownLatch

CountDownLatch是一個同步工具類,它容許一個或多個線程一直等待,直到其餘線程的操做執行完後再執行。在Java併發中,countdownlatch的概念是一個常見的面試題,因此必定要確保你很好的理解了它。在這篇文章中,我將會涉及到在Java併發編 程中跟CountDownLatch相關的如下幾點:

CountDownLatch是什麼? CountDownLatch如何工做? 在實時系統中的應用場景 應用範例 常見的面試題

CountDownLatch是什麼

CountDownLatch是在java1.5被引入的,跟它一塊兒被引入的併發工具類還有CyclicBarrier、Semaphore、ConcurrentHashMap和BlockingQueue,它們都存在於java.util.concurrent包下。CountDownLatch這個類可以使一個線程等待其餘線程完成各自的工做後再執行。例如,應用程序的主線程但願在負責啓動框架服務的線程已經啓動全部的框架服務以後再執行。

CountDownLatch是經過一個計數器來實現的,計數器的初始值爲線程的數量。每當一個線程完成了本身的任務後,計數器的值就會減1。當計數器值到達0時,它表示全部的線程已經完成了任務,而後在閉鎖上等待的線程就能夠恢復執行任務。

9.2 CuclicBarrier

CyclicBarrier和CountDownLatch同樣,都是關於線程的計數器。

9.3 Semaphore

一個計數信號量。從概念上講,信號量維護了一個許可集。若有必要,在許可可用前會阻塞每個 acquire(),而後再獲取該許可。每一個 release() 添加一個許可,從而可能釋放一個正在阻塞的獲取者。可是,不使用實際的許可對象,Semaphore 只對可用許可的號碼進行計數,並採起相應的行動。拿到信號量的線程能夠進入代碼,不然就等待。經過acquire()和release()獲取和釋放訪問許可。
相關方法

acquire
public void acquire()
throws InterruptedException
今後信號量獲取一個許可,在提供一個許可前一直將線程阻塞,不然線程被中斷。獲取一個許可(若是提供了一個)並當即返回,將可用的許可數減 1。
若是沒有可用的許可,則在發生如下兩種狀況之一前,禁止將當前線程用於線程安排目的並使其處於休眠狀態:
某些其餘線程調用此信號量的 release() 方法,而且當前線程是下一個要被分配許可的線程;或者
其餘某些線程中斷當前線程。
若是當前線程:
被此方法將其已中斷狀態設置爲 on ;或者
在等待許可時被中斷。
則拋出 InterruptedException,而且清除當前線程的已中斷狀態。
拋出:
InterruptedException - 若是當前線程被中斷
release
public void release()
釋放一個許可,將其返回給信號量。釋放一個許可,將可用的許可數增長 1。若是任意線程試圖獲取許可,則選中一個線程並將剛剛釋放的許可給予它。而後針對線程安排目的啓用(或再啓用)該線程。
不要求釋放許可的線程必須經過調用 acquire() 來獲取許可。經過應用程序中的編程約定來創建信號量的正確用法。

9.4 Exchanger

Exchanger能夠在兩個線程之間交換數據,只能是2個線程,他不支持更多的線程之間互換數據。

當線程A調用Exchange對象的exchange()方法後,他會陷入阻塞狀態,直到線程B也調用了exchange()方法,而後以線程安全的方式交換數據,以後線程A和B繼續運行

相關文章
相關標籤/搜索