原文鏈接:線程題大全
Java 并發庫同步輔助類
CountDownLatch
工作機制:初始化一個計數器,此計數器的值表示需要等待的事件數量。
提供了兩個主要方法:
- await():當一個線程調用此方法時,它將阻塞,直到計數器的值為 0
- countDown():用于減少計數器的值。通常表示一個事件已經發生了(如任務完成),當計數器的值減到 0 時,所有調用 await()并阻塞的線程將被喚醒并繼續執行
重要特性:
- 不可重置:一旦計數器的值為 0,就不能再被重置回初識值或其他任何值
- 一次性的:計數值到達 0 后,所有在 await()方法上等待的線程將被釋放,而后續的 await()方法調用將立即通過,不會進行阻塞
- 多用途同步工具:能被用于多種目的,等待服務的初始化、一組任務或某個事件的發生
示例:在兩個工作線程結束后再調用主線程
CountDownLatch latch = new CountDownLatch(2); // 設定計數器初始值為2// 創建第一個線程,完成某項任務后調用countDown方法
new Thread(() -> {System.out.println("線程1執行...");latch.countDown();System.out.println("線程1完成操作,計數器減一");
}).start();// 創建第二個線程,也是完成某項任務后調用countDown方法
new Thread(() -> {System.out.println("線程2執行...");latch.countDown();System.out.println("線程2完成操作,計數器減一");
}).start();try {// 調用await方法的線程會被阻塞,直到計數器的值變為0latch.await();System.out.println("兩個線程的操作均已完成,主線程繼續執行");
} catch (InterruptedException e) {e.printStackTrace();
}
CyclicBarrier
工作機制:允許一組線程相互等待到達一個共同屏障點
重要特性:
- 屏障:允許提供一個 Runnable 任務,在所有線程都到達屏障,線程釋放前執行該任務。通常用于合并最終結果或者進行某種必須等到所有線程都到達屏障點后才能執行的操作
- 等待線程數:在創建 CyclicBarrier 時,需要指定等待的線程數量。當指定數量的線程都調用 await()方法,表示它們都到達了屏障點,隨后這些線程都將被釋放
- 超時與中斷:線程在調用 await()方法時可以選擇設置超時時間,超時或者被中斷都將導致線程提前釋放,并拋出相應異常
- 重置:釋放等待線程后重置計數器。
示例:當四個線程都達到屏障后,打印一句話,然后每個線程繼續執行它們的任務
public class CyclicBarrierExample {// 創建一個新的CyclicBarrier,當四個參與者到達時執行屏障操作private CyclicBarrier barrier = new CyclicBarrier(4, () -> System.out.println("所有線程到達屏障點,屏障操作執行!"));public void startTask(String name) {new Thread(() -> {System.out.println(name + "開始執行任務...");// 模擬任務耗時try {Thread.sleep((int)(Math.random() * 1000));} catch (InterruptedException e) {e.printStackTrace();}System.out.println(name + "到達屏障點,等待其他線程...");try {// 調用await方法等待其他線程都到達屏障點barrier.await();} catch (Exception e) {e.printStackTrace();}System.out.println(name + "繼續執行后續操作");}).start();}public static void main(String[] args) {CyclicBarrierExample example = new CyclicBarrierExample();example.startTask("線程A");example.startTask("線程B");example.startTask("線程C");example.startTask("線程D"); // 當所有四個線程達到屏障點,將一起釋放,然后執行屏障操作}
}
線程交叉打印模版
public class CrossPrinter {private int state;private final int printCount;public CrossPrinter(int printCount) {// state用來確定下次打印this.state = 0;// 打印次數this.printCount = printCount;}public void printLetter(String Letter, int crossState ,int curState) {for (int i = 0; i < printCount; i++) {synchronized (this) {while (state % crossState != curState) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName() + ":" + Letter);state++;notifyAll();}}}public static void main(String[] args) {CrossPrinter crossPrinter = new CrossPrinter(5);// Thread A打印"A"new Thread(() -> crossPrinter.printLetter("A", 2,0), "Thread A").start();// Thread B打印"B"new Thread(() -> crossPrinter.printLetter("B", 2,1), "Thread B").start();}}
上述完成了兩線程交叉打印"A"、“B”,具體說明下
- printCount:控制交叉打印次數
- state:全局變量,指明線程已經執行多少次了
- crossState:指明有多少個線程進行交叉
- curState:指明當前線程
- Letter:當前線程打印內容
可用于:
- 多線程交叉打印 A、B、C…
- 兩線程交叉打印奇偶數
三線程交叉打印 A、B、C
模版中是兩線程交叉打印 A、B,只需要做簡單替換就能實現三線程交叉打印 A、B、C
crossState:3
新增線程 C 如下
// Thread A打印"A"
new Thread(() -> crossPrinter.printLetter("A", 3,0), "Thread A").start();// Thread B打印"B"
new Thread(() -> crossPrinter.printLetter("B", 3,1), "Thread B").start();// Thread C打印"C"
new Thread(() -> crossPrinter.printLetter("C", 3,2), "Thread C").start();
兩線程交叉打印奇偶數
比如要求打印到兩線程交叉打印到 10
state 控制線程進行輪次,此時可以換為 while 條件,用來控制跳出循環
crossState:2,表示兩線程
完整代碼如下:
public class CrossPrinter {private int state;private final int printCount;public CrossPrinter(int printCount) {// state用來確定下次打印this.state = 0;// printCount表示打印次數this.printCount = printCount;}public void printNumber(int crossState ,int curState) {while (state < printCount) {synchronized (this) {while (state % crossState != curState) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName() + ":" + state);state++;notifyAll();}}}public static void main(String[] args) {CrossPrinter crossPrinter = new CrossPrinter(10);// Thread A打印偶數new Thread(() -> crossPrinter.printNumber(2,0), "Thread A").start();// Thread B打印奇數new Thread(() -> crossPrinter.printNumber(2,1), "Thread B").start();}}
三線程交叉打印斐波那契數列
新增 oneNum、twoNum 來記錄前兩個數
完整代碼如下
public class CrossPrinter {private int state;private int oneNum;private int twoNum;private final int printCount;public CrossPrinterThree(int printCount) {this.state = 3;this.oneNum = 1;this.twoNum = 1;this.printCount = printCount;}public static void main(String[] args) {CrossPrinterThree crossPrinterThree = new CrossPrinter(10);// 三線程交叉打印斐波那契數列new Thread(() -> crossPrinterThree.printNum(3, 0), "Thread-A").start();new Thread(() -> crossPrinterThree.printNum(3, 1), "Thread-B").start();new Thread(() -> crossPrinterThree.printNum(3, 2), "Thread-C").start();}private void printNum(int crossState, int curState) {while (state < printCount) {synchronized (this) {while (state % crossState != curState) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}int curNum = oneNum + twoNum;System._out_.println(Thread._currentThread_().getName() + ":" + curNum);// 更新前兩個數oneNum = twoNum;twoNum = curNum;state++;notifyAll();}}}}
多線程任務執行 A -> B, A -> C
實現方案:CountDownLatch
- 為線程 B、C 分別設置 CountDownLatch 鎖,當線程 A 執行后,喚醒線程 B、C 的 CountDownLatch 鎖
public class MultiThreadTaskExecution {// 使用兩個初始計數為1的CountDownLatch來實現一對多的通知機制private CountDownLatch latchToB = new CountDownLatch(1);private CountDownLatch latchToC = new CountDownLatch(1);public void taskA() {System.out.println("任務A執行中...");try {Thread.sleep(100); // 模擬任務A執行時間} catch (InterruptedException e) {e.printStackTrace();}System.out.println("任務A執行完畢,通知任務B、C開始執行...");latchToB.countDown();latchToC.countDown();}public void taskB() {try {latchToB.await();System.out.println("任務B執行中...");Thread.sleep(100); // 模擬任務B執行時間System.out.println("任務B執行完畢...");} catch (InterruptedException e) {e.printStackTrace();}}public void taskC() {try {latchToC.await();System.out.println("任務C執行中...");Thread.sleep(100); // 模擬任務C執行時間System.out.println("任務C執行完畢...");} catch (InterruptedException e) {e.printStackTrace();}}public static void main(String[] args) {MultiThreadTaskExecution taskExecution = new MultiThreadTaskExecution();new Thread(taskExecution::taskB).start();new Thread(taskExecution::taskC).start();new Thread(taskExecution::taskA).start();}}
線程 A、B、C 都到達屏障點才執行后續操作
實現方案:CyclicBarrier
- 設置屏障數量 3,同時可設置一個 Runnable 任務,當都達到時輸出一句話。
public class CyclicBarrierOne {// 創建一個新的CyclicBarrier,當3個參與者到達時執行屏障操作private CyclicBarrier barrier = new CyclicBarrier(3, () -> System._out_.println("所有線程到達屏障點,屏障操作執行!"));public static void main(String[] args) {CyclicBarrierOne cyclicBarrierOne = new CyclicBarrierOne();new Thread(() -> cyclicBarrierOne.startTask(), "Thread-A").start();new Thread(() -> cyclicBarrierOne.startTask(), "Thread-B").start();new Thread(() -> cyclicBarrierOne.startTask(), "Thread-C").start();}private void startTask() {System._out_.println(Thread._currentThread_().getName() + "開始執行任務...");try {Thread._sleep_(100);} catch (InterruptedException e) {e.printStackTrace();}System._out_.println(Thread._currentThread_().getName() + "到達屏障點,等待其他線程...");try {barrier.await();} catch (Exception e) {e.printStackTrace();}System._out_.println(Thread._currentThread_().getName() + "繼續執行后續操作");}}
10 個線程同時啟動
public class SimultaneousStart {private static final int _N _= 10;_// 創建一個CountDownLatch用于線程啟動的信號_
_ _private static final CountDownLatch _startSignal _= new CountDownLatch(1);_// 創建一個 CountDownLatch 用于等待所有線程完成的信號_
_ _private static final CountDownLatch _doneSignal _= new CountDownLatch(_N_);public static void main(String[] args) throws InterruptedException {Runnable task = () -> {try {_startSignal_.await(); _// 等待啟動信號_
_ _System._out_.println(Thread._currentThread_().getName() + " has started");Thread._sleep_(2000); _// 模擬任務執行_
_ _System._out_.println(Thread._currentThread_().getName() + " has finished");} catch (InterruptedException e) {Thread._currentThread_().interrupt();} finally {_doneSignal_.countDown(); _// 完成信號_
_ _}};_// 創建并啟動N個線程_
_ _for (int i = 0; i < _N_; i++) {new Thread(task, "Thread-" + (i + 1)).start();}_// 主線程等待片刻,確保所有線程已經啟動并在等待_
_ _Thread._sleep_(1000);System._out_.println("All threads are ready, starting now!");_startSignal_.countDown(); _// 發出啟動信號_
_ doneSignal_.await(); _// 等待所有線程完成__ _System._out_.println("All threads have finished executing.");}
}
死鎖
public class DeadlockExample {// 創建兩個資源
private static final Object _resourceOne _= new Object();
private static final Object _resourceTwo _= new Object();public static void main(String[] args) {new Thread(() -> {synchronized (resourceOne) {System.out.println(Thread.currentThread().getName() + "locked resource1");try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}synchronized (resourceTwo) {System.out.println(Thread.currentThread().getName() + "locked resource2");}}}, "Thread-A").start();new Thread(() -> {synchronized (resourceTwo) {System.out.println(Thread.currentThread().getName() + "locked resource2");try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}synchronized (resourceOne) {System.out.println(Thread.currentThread().getName() + "locked resource1");}}}, "Thread-B").start();}}
多個線程同時爭搶同一把鎖,阻塞情況下喚醒指定線程
- 自定義條件變量
- 標志變量
自定義條件變量
public class CustomLockExample {private final Lock lock = new ReentrantLock();private final Condition conditionA = lock.newCondition();private final Condition conditionB = lock.newCondition();private void methodA() throws InterruptedException {lock.lock();try {System._out_.println("Thread A is waiting");conditionA.await();System._out_.println("Thread A is resumed");} finally {lock.unlock();}}private void methodB() throws InterruptedException {lock.lock();try {System._out_.println("Thread B is waiting");conditionB.await();System._out_.println("Thread B is resumed");} finally {lock.unlock();}}private void resumeA() {lock.lock();try {conditionA.signal(); _// Wake up one thread waiting on conditionA_
_ _System._out_.println("Signaled Thread A");} finally {lock.unlock();}}private void resumeB() {lock.lock();try {conditionB.signal(); _// Wake up one thread waiting on conditionB_
_ _System._out_.println("Signaled Thread B");} finally {lock.unlock();}}public static void main(String[] args) throws InterruptedException {CustomLockExample example = new CustomLockExample();Thread threadA = new Thread(() -> {try {example.methodA();} catch (InterruptedException e) {e.printStackTrace();}});Thread threadB = new Thread(() -> {try {example.methodB();} catch (InterruptedException e) {e.printStackTrace();}});threadA.start();threadB.start();Thread._sleep_(2000); _// Pause to ensure threads reach wait state__ _example.resumeA(); _// Signal threadA_
_ _Thread._sleep_(2000);example.resumeB(); _// Signal threadB_
_ _}
}
標志變量
public class FlagBasedControl {private final Object lock = new Object();private volatile boolean isThreadAWake = false;private void methodA() throws InterruptedException {synchronized (lock) {while (!isThreadAWake) {System._out_.println("Thread A is waiting");lock.wait();}}System._out_.println("Thread A is resumed and resetting flag");isThreadAWake = false; _// Reset the flag for next use }_
_ _}private void resumeA() {synchronized (lock) {isThreadAWake = true;lock.notifyAll(); _// Wake up all threads, but only Thread A will proceed_
_ _System._out_.println("Signaled Thread A");}}public static void main(String[] args) throws InterruptedException {FlagBasedControl example = new FlagBasedControl();Thread threadA = new Thread(() -> {try {example.methodA();} catch (InterruptedException e) {Thread._currentThread_().interrupt();}});threadA.start();Thread._sleep_(2000); _// Pause to ensure thread reaches wait state__ _example.resumeA(); _// Signal threadA_
_ _}
}