問題1 什麼是executor框架
Executor and ExecutorService用於以下場景:
- 創建線程
- 啓動線程
- 管理整個線程的生命週期
Executor創建線程池並管理線程池中線程的生命週期,在executor框架中,Executor接口和ExecutorService接口被頻繁使用。Executor接口定義了execute()方法用於執行命令。ExecutorService接口繼承Executor。
Executor提供了管理終止或產生Future追蹤過程或異步任務的方法,更多內容請閱讀Executor and ExecutorService framework in java。
問題2 executor框架中execute()和submit()的區別
execute()方法 | submit()方法 |
---|---|
execute()方法定義在Executor接口中 | submit()方法定義在ExecutorService接口中 |
用於執行runnable任務 | 用於執行runnable任務或callable任務,提交callable任務返回future對象,通過future.get() 獲取任務的結果 |
方法類型只有一種void execute(Runnable task) |
submit方法有3種形式:<T> Future<T> submit(Callable<T> task) <T> Future<T> submit(Runnable task, T result) Future<?> submit(Runnable task) |
問題3 什麼是Semaphore?
Semaphore通過許可控制共享資源的訪問:
- 如果許可permits大於0,semaphore允許訪問共享資源;
- 如果許可permits小等於0,semaphore不允許訪問共享資源;
其中許可有點類似計數器,用來允許訪問共享資源,爲此,爲了訪問該資源,線程必須從semaphore那裏授權許可。
Semaphore有兩個構造函數:
1. Semaphore(int permits)
:permits是初始的可用許可,這個值有可能是負值,表示此刻等待獲取共享資源的線程數。如果permits=1,表示只有一個線程正在使用共享資源。
2. Semaphore(int permits, boolean fair)
:設置fair,能確保等待線程按請求順序被授權。
acquire( )方法有兩種形式:
1. void acquire( ) throws InterruptedException
:獲取一個可用許可,並且可用許可減1。
2. void acquire(int permits) throws InterruptedException
:獲取permits個可用許可,同時可用許可減permits。如果permits個許可不可用,那麼當前線程處於休眠狀態,直到發生如下事情:
- 其他線程調用release()
方法,可用許可大等於permits;
- 或者其他線程中斷該線程。
release( )方法有兩種形式:
1. void release( )
:釋放許可,並且許可加1。
2. void release(int permits)
:釋放permits個許可,並且可用許可增加permits個。
有關Semaphor請閱讀Semaphore in java。
問題4 利用Semaphore實現生產者消費者模式
請閱讀:Semaphore used for implementing Producer Consumer pattern
問題5 實現自己的Semaphore
請閱讀:Implementation of custom/own Semaphore in java
問題6 Java 7中atomic類的意義
Java在java.util.concurrent.atomic中提供了其他的同步類,這些類有AtomicInteger
、AtomicLong
、AtomicBoolean
等,相應的方法有get()
,set()
,getAndSet()
,compareAndSet( )
,decrementAndGet( )
等。在多線程環境下,這些類不需要顯示地進行同步,因爲它們是線程安全的。
atomic的更多內容請參考Atomic operations in java。
問題7 Future和Callable之間的關聯?
Future<V>
接口提供的方法用於返回計算的結果,計算未完成會一直等待,或者取消計算任務。
Callable<V>
接口提供了計算結果的方法,並返回計算結果,或者拋出異常。任何實現Callable接口的類必須重寫call()方法。
向Executor提交Callable對象返回的對象類型是Future,如:
Future<Double> futureDouble=executor.submit(new SquareDoubleCallable(2.2));
更多內容請閱讀Executor and ExecutorService framework in java。
問題8 java.util.concurrent.Callable和java.lang.Runnable不同?
實現Callable接口的類必須重寫call()方法,call()方法返回計算結果或者無法執行拋出異常;
實現Runnable接口的類必須重寫run()方法,run()方法不會返回計算結果,也不拋出檢查異常。
問題9 CountDownLatch
CountDownLatch適用於當前線程等待一個或多個線程完成特定的操作。CountDownLatch(int count) 初始化需要確定在latch釋放之前發生事件的個數。事件每次發生count減1,一旦count減爲0,latch被釋放。
CountDownLatch的await()方法有兩種形式:
- void await( ) throws InterruptedException
引起當前線程等待,直到latch count減爲0,或者當前線程被中斷。
- boolean await(long timeout, TimeUnit unit)
引起當前線程等待,直到latch count減爲0,或者當前線程被中斷,或者超時。
countDown()方法用來減少latch count計數。count減爲0,所有等待線程被釋放。
瞭解更多請閱讀CountDownLatch in java。
問題10 CountDownLatch的使用場景
問題11 如何實現CountDownLatch?
請閱讀Implementation of custom/own CountDownLatch in java。
問題12 CyclicBarrier
CyclicBarrier用於一個或多個線程完成都完成特定的操作才觸發一個事件。2個或者多個線程彼此等待直到到達共同的barrier point。當所有的線程到達共同的barrier point(即所有的線程調用await()方法):
- 所有的等待線程被釋放;
- 事件被觸發。
CyclicBarrier的構造函數:
- CyclicBarrier(int parties)
新的CyclicBarrier被創建,當parties個等待線程到達共同的barrier point,等待的線程被釋放。
- CyclicBarrier(int parties, Runnable barrierAction)
新的CyclicBarrier被創建,當parties個等待線程到達共同的barrier point,等待的線程被釋放,barrierAction事件被觸發。
CyclicBarrier的await()方法:
- int await() throws InterruptedException, BrokenBarrierException
- int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException
當前的線程如果不是最後到達的(即調用await()方法),它會等待直到發生如下事情:
1. 最後一個線程調用await()方法
2. 其他線程中斷了當前線程
3. 其他線程中斷了其中的一個等待線程
4. 等待barrier的過程中一些線程超時
5. 在這個cyclicBarrier上的線程調用了reset()方法
6. 經過了timeout時間
更多內容請閱讀CyclicBarrier in java。
問題13 爲什麼CyclicBarrier能夠循環?
barrier之所以能夠循環,是因爲CyclicBarrier能夠重用,當所有的等待線程釋放,或者事件已被觸發。
問題14 在實際場景中什麼時候使用CyclicBarrier?
舉例:有10個好朋友打算去地點A野餐(朋友是線程,地點A是共同的barrier point),他們決定玩某個遊戲當所有的人員到達地點A(遊戲是一個事件)。所以10個朋友必須等到他們都到齊才能進行遊戲。
問題15 如何實現CyclicBarrier?
請參考Implementation of custom/own CyclicBarrier in java。
問題16 CyclicBarrier和CountDownLatch之間的異同
- CyclicBarrier和CountDownLatch有點相似,是因爲它們要等待特定數量的線程到達某個點,然後使count或者parties等於0。但是CountDownLatch完成等待,線程必須調用countDown()方法;而CyclicBarrier完成等待,線程必須調用await()方法。
- 它們的構造函數區別:
CountDownLatch(int count) :count確定latch釋放之前發生的事件個數;
CyclicBarrier(int parties):parties個線程相互等待到達共同的barrier point,當所有的線程到達這個共同的barrier point,parties個等待線程被釋放。 - CyclicBarrier可以重複等待,而CountDownLatch不能,即一旦count減爲0,cyclicBarrier可重複使用。
- CyclicBarrier可以觸發事件。
問題17 Phaser是什麼?
Phaser在功能上和CyclicBarrier和CountDownLatch有點相似,但是比它倆更加靈活。
Phaser爲我們提供了更加靈活的註冊和取消註冊。對於註冊,可以使用下面的方式:
- 構造函數
- int register()
- bulkRegister()
對於取消註冊,可以使用下面的方式:
- arriveAndDeregister()
可以使用getPhase()方法返回當前的phase個數,isTerminated()返回phaser時候結束。
更多內容請閱讀Phaser in java。
問題18 Phaser和CyclicBarrier之間的異同
CyclicBarrier和Phaser都可以重複等待。在CyclicBarrier中,在構造函數中註冊,但是Phaser提供了在任何時候註冊和取消註冊的方法。
問題19 Phaser的arrive()方法和arriveAndAwaitAdvance() 的區別
Phaser的arrive()方法不會引起當前線程等待其他線程完成當前的phase,即當前線程可以立刻進入下一個phase,無須等待其他註冊線程完成當前的phase。
而arriveAndAwaitAdvance()方法會讓當前線程去等待其他註冊線程完成當前的phase,即只有當其他的所有線程完成當前的phase,當前線程纔可以進行下一個phase。
問題20 phaser什麼時候結束
調用Phaser的arriveAndDeregister() 方法會讓註冊的線程數變爲0。當onAdvance()方法返回true,終止也會被觸發。
問題21 如何控制phase的個數
問題22 Phaser的使用場景
軟件開發管理按如下phase進行:
1. 需求收集;
2. 軟件開發;
3. 測試;
第二個phase不會開始直到第一個phase完成,第三個phase也不會開始直到第二個phase完成。
問題23 每次phaser註冊的最大數量
每次phaser註冊的數量爲65535,如果想註冊更多的線程,將會拋出IllegalStateException。
問題24 Exchanger
Exchanger能夠使兩個線程交換彼此的數據,它能夠非常容易地實現生產者消費者模式,生產者和消費者線程交換彼此的數據。
exchange(V x)方法能夠使兩個線程交換彼此的數據,如果當前線程是第一個調用exchange()方法,那它會等待直到其他線程調用exchange()方法,或者其他線程中斷當前線程。如果其他線程已經調用了exchange()方法,那麼它會恢復執行,等待線程恢復並接受當前線程的數據,或者當前線程收到其他等待線程的數據,立刻返回。
更多內容請閱讀Exchanger in java。
問題25 用Exchanger實現生產者消費者模式
請參考Read program to implement Producer Consumer pattern using Exchanger。
問題26 使用BlockingQueue解決生產者消費者模式
請參考BlockingQueue is a interface and we will use its implementation class LinkedBlockingQueue。
問題27 使用BlockingQueue解決生產者消費者模式
請參考Producer Consumer pattern using Custom implementation of LinkedBlockingQueue interface。
問題28 java Lock
java.util.concurrent.locks.Locks是一個接口,它的實現類提供了更實用的鎖操作。lcok有助於控制多線程共享資源的訪問,每次只有一個線程獲取鎖,並且訪問共享資源。如果第二個線程試圖訪問鎖上的共享資源,當這個鎖被另一個線程獲取,那麼第二個線程處於等待,直到鎖被釋放。這樣就可以實現同步和避免競爭。
更多內容請閱讀locks and ReEntrantLocks in java。
問題29 Lcok 接口中的關鍵方法
void lock()
如果鎖沒有被其他線程佔用,獲取鎖。設置鎖計數爲1;如果當前線程已經佔用了鎖,那麼說計數器加1;如果鎖被其他線程佔用,那麼當前線程等待直到鎖被釋放。
void unLock()
如果當前線程佔用了鎖,那麼鎖計數器減1;如果鎖計數器減爲0,那麼鎖被釋放;如果鎖計數器大於0,那麼鎖沒有被釋放;如果當前線程沒佔用鎖,那麼拋出IllegalMonitorStateException異常。
boolean tryLock()
獲取鎖如果沒有被其他線程佔用,並返回true,設置鎖計數器設置爲1。如果當前線程已經佔有了鎖,那麼返回true,並鎖計數器加1.如果鎖被其他線程佔用,返回false。
boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException
獲取鎖如果沒有被其他線程佔用,返回true,設置鎖計數器爲1。如果當前線程已經佔有了鎖,返回true,並且鎖計數器加1。如果鎖被其他線程佔用,那麼當前線程等待直到發生如下:1、另一個線程釋放鎖,當前線程獲取鎖;2、其他線程中斷了當前線程;3、設定的時間超時。
Condition newCondition()
返回Condition實例,被用於鎖接口。Condition實例類似於wait(), notify(), notifyAll()方法。
更多請閱讀locks in java。