面试中的线程题
原文链接:线程题大全
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_
_ _}
}