執行緒是 Java執行的最小單元,通常意義上來說,多個執行緒是爲了加快速度且無需保序,這篇文章,我們來分析一道面試題目:如要保證執行緒T1, T2, T3順序執行?
考察意圖
在面試中出現這道問題,通常是爲了考察候選人的以下幾個知識點:
1. 多執行緒基礎知識: 希望瞭解候選人是否熟悉Java多執行緒的基本概念,包括執行緒的建立、啟動和同步機制。
2. 同步機制的理解:候選人需要展示對Java中各種同步工具的理解,如join()
、CountDownLatch
、Semaphore
、CyclicBarrier
等,並知道如何在不同場景下應用這些工具。
3. 執行緒間通訊:希望候選人理解執行緒間通訊的基本原理,例如如何使用wait()
和notify()
來協調執行緒。
4. 對Java併發包的熟悉程度: 希望候選人瞭解Java併發包(java.util.concurrent
)中的工具和類,展示其對現代Java併發程式設計的掌握。
保證執行緒順序執行的方法
在分析完面試題的考察意圖之後,我們再分析如何保證執行緒順序執行,這裏列舉了幾種常見的方式。
join()
join()
方法是Thread類的一部分,可以讓一個執行緒等待另一個執行緒完成執行。 當你在一個執行緒T上呼叫T.join()時,呼叫執行緒將進入等待狀態,直到執行緒T完成(即終止)。因此,可以透過在每個執行緒啟動後呼叫join()
來實現順序執行。
如下示例程式碼,展示了join()
如何保證執行緒順序執行:
Thread t1 = new Thread(() -> { // 執行緒T1的任務 }); Thread t2 = new Thread(() -> { // 執行緒T2的任務 }); Thread t3 = new Thread(() -> { // 執行緒T3的任務 }); t1.start(); t1.join(); // 等待t1完成 t2.start(); t2.join(); // 等待t2完成 t3.start(); t3.join(); // 等待t3完成
CountDownLatch
CountDownLatch
透過一個計數器來實現,初始時,計數器的值由建構函式設定,每次呼叫countDown()方法,計數器的值減1。當計數器的值變為零時,所有等待在await()方法上的執行緒都將被喚醒,繼續執行。
CountDownLatch
是Java併發包(java.util.concurrent)中的一個同步輔助類,用於協調多個執行緒之間的執行順序。它允許一個或多個執行緒等待另外一組執行緒完成操作。
如下示例程式碼,展示了CountDownLatch
如何保證執行緒順序執行:
CountDownLatch latch1 = new CountDownLatch(1); CountDownLatch latch2 = new CountDownLatch(1); Thread t1 = new Thread(() -> { // 執行緒T1的任務 latch1.countDown(); // 完成後遞減latch1 }); Thread t2 = new Thread(() -> { try { latch1.await(); // 等待T1完成 // 執行緒T2的任務 latch2.countDown(); // 完成後遞減latch2 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); Thread t3 = new Thread(() -> { try { latch2.await(); // 等待T2完成 // 執行緒T3的任務 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); t1.start(); t2.start(); t3.start();
CountDownLatch
關鍵方法解析:
CountDownLatch(int count) : 建構函式,建立一個CountDownLatch例項,計數器的初始值為count。
void await() : 使當前執行緒等待,直到計數器的值變為零。
boolean await(long timeout, TimeUnit unit) : 使當前執行緒等待,直到計數器的值變為零或等待時間超過指定的時間。
void countDown() : 遞減計數器的值。當計數器的值變為零時,所有等待的執行緒被喚醒。
Semaphore
Semaphore
透過一個計數器來管理許可,計數器的初始值由建構函式指定,表示可用許可的數量。執行緒可以透過呼叫acquire()
方法請求許可,如果許可可用則授予訪問許可權,否則執行緒將阻塞。使用完資源後,執行緒呼叫release()
方法釋放許可,從而允許其他阻塞的執行緒獲取許可。
如下示例程式碼,展示了Semaphore
如何保證執行緒順序執行:
Semaphore semaphore1 = new Semaphore(0); Semaphore semaphore2 = new Semaphore(0); Thread t1 = new Thread(() -> { // 執行緒T1的任務 semaphore1.release(); // 釋放一個許可 }); Thread t2 = new Thread(() -> { try { semaphore1.acquire(); // 獲取許可,等待T1完成 // 執行緒T2的任務 semaphore2.release(); // 釋放一個許可 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); Thread t3 = new Thread(() -> { try { semaphore2.acquire(); // 獲取許可,等待T2完成 // 執行緒T3的任務 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); t1.start(); t2.start(); t3.start();
Semaphore
關鍵方法分析:
Semaphore(int permits) :構造一個具有給定許可數的Semaphore。
Semaphore(int permits, boolean fair) :構造一個具有給定許可數的Semaphore,並指定是否是公平的。公平性指的是執行緒獲取許可的順序是否是先到先得。
void acquire() :獲取一個許可,如果沒有可用許可,則阻塞直到有許可可用。
void acquire(int permits) :獲取指定數量的許可。
void release() :釋放一個許可。
void release(int permits) :釋放指定數量的許可。
int availablePermits() :返回當前可用的許可數量。
boolean tryAcquire() :嘗試獲取一個許可,立即返回true或false。
boolean tryAcquire(long timeout, TimeUnit unit) :在給定的時間內嘗試獲取一個許可。
單執行緒池
單執行緒池(Executors.newSingleThreadExecutor())可以確保任務按提交順序依次執行。所有任務都會在同一個執行緒中執行,保證了順序性。
如下示例程式碼展示了單執行緒池如何保證執行緒順序執行:
ExecutorService executor = Executors.newSingleThreadExecutor(); executor.submit(new T1()); executor.submit(new T2()); executor.submit(new T3()); executor.shutdown();
單執行緒這種方法簡單易用,適合需要順序執行的場景。
synchronized
synchronized 是Java中的一個關鍵字,用於實現執行緒同步,確保多個執行緒對共享資源的訪問是互斥的。它透過鎖機制來保證同一時刻只有一個執行緒可以執行被Synchronized保護的程式碼塊,從而避免資料不一致和執行緒安全問題。
如下示例程式碼,展示了synchronized
如何保證執行緒順序執行:
class Task { synchronized void executeTask(String taskName) { System.out.println(taskName + " 執行"); } } public class Main { public static void main(String[] args) { Task task = new Task(); new Thread(() -> task.executeTask("T1")).start(); new Thread(() -> task.executeTask("T2")).start(); new Thread(() -> task.executeTask("T3")).start(); } }
總結
本文,我們分析了5種保證執行緒T1,T2,T3順序執行的方法,依次如下:
join()
CountDownLatch
Semaphore
單執行緒池
synchronized
在實際開發中,這種需要在業務程式碼中去保證執行緒執行順序的情況幾乎不會出現,因此,這個面試題其實缺乏實際的應用場景,純粹是爲了面試存在。儘管是面試題,還是可以幫助我們更好地去了解和掌握執行緒。