《Java并发工具包:从面试被虐到吊打面试官的奇幻之旅》
面试官(推了推眼镜,镜片闪过一道寒光):"听说你熟悉Java并发?那你知道CountDownLatch和CyclicBarrier的区别吗?"
你(额头冒汗,大脑飞速运转):"呃...一个用一次,一个能循环用?"
面试官(冷笑):"就这?"
别慌!今天我们就用一场"面试情景剧",带你彻底搞懂Java并发工具包(JUC),让你下次面试时反向让面试官怀疑人生!
第一幕:CountDownLatch —— 五黑等队友的绝望
面试官:"假设你正在开发一个王者荣耀组队系统,5人组队成功后才能开始游戏,你怎么实现?"
你(灵光一闪):"用CountDownLatch!就像等队友点'准备'一样!"
public class WangZheTeam {
public static void main(String[] args) throws InterruptedException {
int playerCount = 5;
CountDownLatch latch = new CountDownLatch(playerCount);
for (int i = 1; i <= playerCount; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "已准备");
latch.countDown(); // 队友点准备了!
}, "玩家" + i).start();
}
latch.await(); // 主线程阻塞等待
System.out.println("全军出击!");
}
}
设计思路:
- 就像游戏里的"准备确认"弹窗,每个玩家点准备就countDown()
- 当计数器归零(所有人都准备),await()解除阻塞
灵魂拷问:如果有个队友永远不点准备怎么办?(答案:用await(long timeout, TimeUnit unit)设置超时!)
第二幕:CyclicBarrier —— 旅游团的"集合点"模式
面试官(突然变脸):"那如果导游要等所有游客在景点A集合后,再去景点B呢?"
你(邪魅一笑):"这次用CyclicBarrier,还能重复使用!"
public class TouristGuide {
public static void main(String[] args) {
int touristNum = 3;
CyclicBarrier barrier = new CyclicBarrier(touristNum,
() -> System.out.println("所有人到齐,出发下一个景点!"));
for (int i = 1; i <= touristNum; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + "到达集合点");
barrier.await(); // 等待其他游客
System.out.println(Thread.currentThread().getName() + "继续游览");
} catch (Exception e) {
e.printStackTrace();
}
}, "游客" + i).start();
}
}
}
爆笑时刻:
当最后一个游客调用await()时,所有线程同时解除阻塞,就像导游大喊:"3号家庭终于上完厕所了,我们走!"
进阶技巧:
- 可以重置reset()(但会引发BrokenBarrierException)
- 比较适合MapReduce分阶段处理的场景
第三幕:Semaphore —— KTV的残酷现实
面试官(突然唱起来):"爱情不是你想买~ 想买就能买~ 现在KTV只有2个麦克风,10个人要唱歌怎么办?"
你(拍桌而起):"用Semaphore!比抢麦打架文明多了!"
public class KTV {
public static void main(String[] args) {
int micNum = 2;
int peopleNum = 10;
Semaphore semaphore = new Semaphore(micNum);
for (int i = 1; i <= peopleNum; i++) {
new Thread(() -> {
try {
semaphore.acquire(); // 抢到麦克风!
System.out.println(Thread.currentThread().getName() + ":在唱《青藏高原》");
Thread.sleep(2000); // 唱high了不肯放手
System.out.println(Thread.currentThread().getName() + ":唱完了(不情愿)");
semaphore.release(); // 终于释放麦克风
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "麦霸" + i).start();
}
}
}
血泪教训:
- 这就是为什么KTV要按小时收费——tryAcquire(long timeout, TimeUnit unit)
- 数据库连接池也是这个原理!(突然升华)
第四幕:Exchanger —— 二手市场的交易艺术
面试官(突然摆地摊):"现在你是闲鱼程序员,如何实现两个人交换物品?"
你(掏出键盘):"Exchanger,比线下见面交易安全多了!"
public class XianYu {
public static void main(String[] args) {
Exchanger<String> exchanger = new Exchanger<>();
new Thread(() -> {
try {
String fromThread2 = exchanger.exchange("PS5");
System.out.println("线程1用PS5换到了:" + fromThread2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
String fromThread1 = exchanger.exchange("Switch");
System.out.println("线程2用Switch换到了:" + fromThread1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
防坑指南:
- 如果只有一个线程调用exchange(),它会等到天荒地老(就像你挂的二手商品没人问津)
- 适合两个线程间的数据交换(遗传算法中常用)
终极大招:Phaser —— 多阶段世界杯
面试官(穿上球衣):"现在模拟世界杯:小组赛->淘汰赛->决赛,各阶段要等所有比赛结束!"
你(直接跪了):"师傅别念了...我用Phaser还不行吗!"
public class WorldCup {
public static void main(String[] args) {
Phaser phaser = new Phaser(4) { // 4支球队
protected boolean onAdvance(int phase, int parties) {
System.out.println("第" + (phase + 1) + "阶段结束");
return phase >= 2 || parties == 0; // 共3个阶段
}
};
for (int i = 1; i <= 4; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ":小组赛拼杀");
phaser.arriveAndAwaitAdvance(); // 等所有球队完成
if(Thread.currentThread().getName().contains("4")) {
System.out.println("球队4被淘汰了");
phaser.arriveAndDeregister(); // 注销
return;
}
System.out.println(Thread.currentThread().getName() + ":淘汰赛血战");
phaser.arriveAndAwaitAdvance();
System.out.println(Thread.currentThread().getName() + ":决赛!");
}, "球队" + i).start();
}
}
}
设计精髓:
- 动态注册/注销参与者(register()/arriveAndDeregister())
- 比CyclicBarrier更灵活的分阶段控制
课后思考题(让你装X用)
- 为什么CyclicBarrier的构造方法可以传Runnable,而CountDownLatch不行?
(提示:想想执行时机和线程归属) - 如何用Semaphore实现一个"过独木桥"问题,桥最多承重3人?
(提示:new Semaphore(3, true)第二个参数是公平模式) - StampedLock的乐观读是什么黑魔法?
(提示:类似数据库乐观锁,适合读多写少场景)
下次面试时,当面试官问到并发工具,你可以优雅地反问:"您想听哪种场景的实现?我有五种方案可以对比。" 深藏功与名!