Top 50 Java 線程併發的面試問題

問題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中提供了其他的同步類,這些類有AtomicIntegerAtomicLongAtomicBoolean等,相應的方法有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的使用場景

請閱讀CountDownLatch in java

問題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之間的異同

  1. CyclicBarrierCountDownLatch有點相似,是因爲它們要等待特定數量的線程到達某個點,然後使count或者parties等於0。但是CountDownLatch完成等待,線程必須調用countDown()方法;而CyclicBarrier完成等待,線程必須調用await()方法。
  2. 它們的構造函數區別:
    CountDownLatch(int count) :count確定latch釋放之前發生的事件個數;
    CyclicBarrier(int parties):parties個線程相互等待到達共同的barrier point,當所有的線程到達這個共同的barrier point,parties個等待線程被釋放。
  3. CyclicBarrier可以重複等待,而CountDownLatch不能,即一旦count減爲0,cyclicBarrier可重複使用。
  4. CyclicBarrier可以觸發事件。

問題17 Phaser是什麼?

Phaser在功能上和CyclicBarrierCountDownLatch有點相似,但是比它倆更加靈活。
Phaser爲我們提供了更加靈活的註冊和取消註冊。對於註冊,可以使用下面的方式:
- 構造函數
- int register()
- bulkRegister()
對於取消註冊,可以使用下面的方式:
- arriveAndDeregister()
可以使用getPhase()方法返回當前的phase個數,isTerminated()返回phaser時候結束。
更多內容請閱讀Phaser in java

問題18 Phaser和CyclicBarrier之間的異同

CyclicBarrierPhaser都可以重複等待。在CyclicBarrier中,在構造函數中註冊,但是Phaser提供了在任何時候註冊和取消註冊的方法。

問題19 Phaser的arrive()方法和arriveAndAwaitAdvance() 的區別

Phaser的arrive()方法不會引起當前線程等待其他線程完成當前的phase,即當前線程可以立刻進入下一個phase,無須等待其他註冊線程完成當前的phase。
而arriveAndAwaitAdvance()方法會讓當前線程去等待其他註冊線程完成當前的phase,即只有當其他的所有線程完成當前的phase,當前線程纔可以進行下一個phase。

問題20 phaser什麼時候結束

調用Phaser的arriveAndDeregister() 方法會讓註冊的線程數變爲0。當onAdvance()方法返回true,終止也會被觸發。

問題21 如何控制phase的個數

請參考:Program to demonstrate usage of how we can override Phaser’s onAdvance method to control number of phase we want to execute

問題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

原文:Thread concurrency - Top 50 interview questions and answer in java for fresher and experienced (detailed explanation with programs)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章