# java_maven_concurrent **Repository Path**: enzoism/java_maven_concurrent ## Basic Information - **Project Name**: java_maven_concurrent - **Description**: java_maven_concurrent - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-04 - **Last Updated**: 2026-09-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # AI问答记录 > 生成时间: 2026/9/4 10:00:41 [TOC] --- # 1-java多线程的通信方式有哪些,帮我整理,给出核心的代码示例,最后帮我总结并给出记忆技巧 # Java 多线程通信方式详解 Java 中线程通信的本质是:**一个线程修改了共享状态,另一个线程能够感知到这个变化并做出响应**。下面按从基础到高级的顺序整理。 --- ## 一、基于 Object 监视器:`wait / notify / notifyAll` > 最原始的线程协作方式,必须在 `synchronized` 代码块内使用。 ```java public class WaitNotifyDemo { private boolean flag = false; public synchronized void produce() throws InterruptedException { while (flag) { wait(); // 生产者等待消费 } // 生产数据 flag = true; System.out.println("生产完成"); notifyAll(); // 唤醒消费者 } public synchronized void consume() throws InterruptedException { while (!flag) { wait(); // 消费者等待生产 } // 消费数据 flag = false; System.out.println("消费完成"); notifyAll(); // 唤醒生产者 } } ``` **关键点**:`wait()` 会释放锁,`notify()` 不会立即释放锁(需等同步块结束)。 --- ## 二、显式锁 + Condition:`Lock + Condition` > `java.util.concurrent.locks` 包提供,比 wait/notify 更灵活(可创建多个 Condition)。 ```java import java.util.concurrent.locks.*; public class ConditionDemo { private final Lock lock = new ReentrantLock(); private final Condition notFull = lock.newCondition(); private final Condition notEmpty = lock.newCondition(); private boolean flag = false; public void produce() throws InterruptedException { lock.lock(); try { while (flag) notFull.await(); flag = true; System.out.println("生产完成"); notEmpty.signal(); // 只唤醒消费者 } finally { lock.unlock(); } } public void consume() throws InterruptedException { lock.lock(); try { while (!flag) notEmpty.await(); flag = false; System.out.println("消费完成"); notFull.signal(); // 只唤醒生产者 } finally { lock.unlock(); } } } ``` --- ## 三、阻塞队列:`BlockingQueue` > 生产者-消费者模式的"终极解决方案",内置了线程安全与阻塞逻辑。 ```java import java.util.concurrent.*; public class BlockingQueueDemo { private final BlockingQueue queue = new LinkedBlockingQueue<>(10); public void produce() throws InterruptedException { queue.put("数据"); // 队列满时自动阻塞 System.out.println("生产: 数据"); } public void consume() throws InterruptedException { String data = queue.take(); // 队列空时自动阻塞 System.out.println("消费: " + data); } } ``` --- ## 四、并发工具类 ### 4.1 CountDownLatch(倒计时门闩) > 一个线程等待多个线程完成。 ```java CountDownLatch latch = new CountDownLatch(3); // 每个子线程执行完调用 latch.countDown(); // 主线程调用 latch.await() 等待 ``` ### 4.2 CyclicBarrier(循环栅栏) > 多个线程互相等待,到达同一屏障点后同时继续。 ```java CyclicBarrier barrier = new CyclicBarrier(3); // 每个线程执行到 barrier.await() 时阻塞,等够3个后一起放行 ``` ### 4.3 Semaphore(信号量) > 控制同时访问某资源的线程数量。 ```java Semaphore semaphore = new Semaphore(2); // 最多2个线程同时执行 semaphore.acquire(); // 获取许可 semaphore.release(); // 释放许可 ``` ### 4.4 Exchanger(交换器) > 两个线程在某个同步点交换数据。 ```java Exchanger exchanger = new Exchanger<>(); String data = exchanger.exchange("我的数据"); // 阻塞直到另一个线程也调用 exchange ``` --- ## 五、Future / CompletableFuture(异步结果通信) > 主线程获取子线程的执行结果。 ```java // Future 方式 ExecutorService executor = Executors.newSingleThreadExecutor(); Future future = executor.submit(() -> { Thread.sleep(1000); return 42; }); Integer result = future.get(); // 阻塞等待结果 // CompletableFuture 方式(推荐) CompletableFuture.supplyAsync(() -> "Hello") .thenApply(s -> s + " World") .thenAccept(System.out::println); ``` --- ## 六、volatile(可见性通信) > 一个线程修改 `volatile` 变量,另一个线程立即可见。 ```java public class VolatileDemo { private volatile boolean running = true; public void stop() { running = false; // 另一个线程会立刻看到这个变化 } public void run() { while (running) { // 执行任务 } } } ``` **注意**:`volatile` 只保证可见性,不保证原子性,不能替代锁。 --- ## 七、管道流:`PipedInputStream / PipedOutputStream` > 线程间通过"管道"直接传输字节/字符数据。 ```java PipedInputStream in = new PipedInputStream(); PipedOutputStream out = new PipedOutputStream(); out.connect(in); // 建立连接 // 线程A: out.write(data); // 线程B: in.read(data); ``` --- ## 八、ThreadLocal(线程本地变量) > 严格来说不是"线程间通信",但可以实现线程上下文的"隐式传递"。 ```java ThreadLocal context = new ThreadLocal<>(); context.set("用户A"); // 只有当前线程能读到这个值 String user = context.get(); ``` --- ## 📊 总结对比表 | 通信方式 | 适用场景 | 特点 | |---------|---------|------| | `wait/notify` | 简单的生产者-消费者 | JDK 最原始,需配合 synchronized | | `Lock + Condition` | 需要多条件精确唤醒 | 灵活,可中断、可超时、可创建多个 Condition | | `BlockingQueue` | 标准生产者-消费者 | **最推荐**,代码最简洁,线程安全内置 | | `CountDownLatch` | 一个线程等 N 个线程 | 一次性,计数不能重置 | | `CyclicBarrier` | N 个线程互相等待 | 可循环使用,适合分阶段计算 | | `Semaphore` | 限流、资源池 | 控制并发数量 | | `Exchanger` | 两个线程交换数据 | 场景较窄 | | `Future/CompletableFuture` | 获取异步执行结果 | CompletableFuture 支持链式组合 | | `volatile` | 状态标志位、开关 | 轻量,只保证可见性 | | `PipedStream` | 直接传输数据流 | 使用较少,IO 场景 | --- ## 🧠 记忆技巧 用一个故事来记: > **一个工厂(多线程)要生产玩具并卖出去**: 1. **老办法**:工人 A 和工人 B 站在仓库门口(`synchronized`),A 生产完了**喊一声**(`notify`),B 听到后进来拿(`wait/notify`)—— 这是最早的沟通方式。 2. **升级办法**:仓库装上了**智能门锁**(`Lock`),还分了两个门铃:一个"货满了"(`notFull`),一个"有货了"(`notEmpty`),按对门铃才开门(`Condition`)。 3. **终极办法**:直接换成**自动传送带**(`BlockingQueue`),满了自动停,空了自动等,根本不用人喊。 4. **厂长查岗**:厂长(主线程)派了 3 个小组去干活,自己坐在办公室等**汇报**(`CountDownLatch`),3 组都汇报完了才下班。 5. **接力赛**:3 个小组要**一起跑到终点**才能进入下一轮(`CyclicBarrier`)。 6. **限流**:工厂只有 2 台机器(`Semaphore`),最多同时 2 个人用。 7. **交换情报**:两个间谍在公园**交换公文包**(`Exchanger`)。 8. **远程办公**:员工在家干活,干完了**发邮件交结果**(`Future`),还支持把多个任务**串起来**自动处理(`CompletableFuture`)。 9. **广播通知**:墙上挂了个**电子显示屏**(`volatile`),所有人随时能看到最新状态。 10. **输油管道**:两个车间之间接了根**管子**直接传东西(`PipedStream`)。 --- **一句话选择指南**: - 做**生产者-消费者** → 首选 `BlockingQueue` - 需要**精确控制唤醒条件** → `Lock + Condition` - 一个等多个 / 多个互相等 → `CountDownLatch` / `CyclicBarrier` - 要**异步结果** → `CompletableFuture` - 只是**状态开关** → `volatile` --- # 2-五个人等待小明完成工作后开始工作,和小明等待5个人完成工作后开始工作,我是该使用CountDownLatch(倒计时门闩)还是 CyclicBarrier(循环栅栏) 这两个场景都应该用 **CountDownLatch**,而不是 CyclicBarrier。 --- ## 核心区别一句话 | | CountDownLatch | CyclicBarrier | |---|---|---| | 本质 | **一个线程等待 N 个线程** 完成 | **N 个线程互相等待** 彼此 | | 方向 | 单向等待(主等子 / 子等主) | 双向互相等待(大家等大家) | | 能否复用 | 一次性(不能重置) | 可循环复用 | --- ## 场景一:5 个人等小明 > 小明是"被等待者",5 个人是"等待者" ```java CountDownLatch latch = new CountDownLatch(1); // 只需要等 1 个人(小明) // 5 个工人线程 for (int i = 0; i < 5; i++) { new Thread(() -> { try { latch.await(); // 5 个人都在这里等着 System.out.println("小明干完了,我开始干活!"); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } // 小明线程 new Thread(() -> { System.out.println("小明正在干活..."); latch.countDown(); // 小明干完了,通知所有人 }).start(); ``` --- ## 场景二:小明等 5 个人 > 小明是"等待者",5 个人是"被等待者" ```java CountDownLatch latch = new CountDownLatch(5); // 需要等 5 个人 // 5 个工人线程 for (int i = 0; i < 5; i++) { new Thread(() -> { System.out.println("工人干活中..."); latch.countDown(); // 干完一个,计数减 1 }).start(); } // 小明线程 new Thread(() -> { try { latch.await(); // 等 5 个人都 countDown 完 System.out.println("5 个人都干完了,小明开始收尾!"); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); ``` --- ## 为什么不用 CyclicBarrier? CyclicBarrier 的典型场景是 **"大家等大家"**,比如: > 5 个工人约好:所有人都到齐后,一起开工。每个人到了都喊一声"我到了",然后等着;等第 5 个人也到了,5 个人**同时**开始干活。 ```java CyclicBarrier barrier = new CyclicBarrier(5); for (int i = 0; i < 5; i++) { new Thread(() -> { System.out.println("我到了,等人齐..."); barrier.await(); // 5 个人都要调用 await System.out.println("人齐了,一起开工!"); }).start(); } ``` 注意:**CyclicBarrier 要求所有参与方都调用 `await()`**。 而你的两个场景中,小明和 5 个人是"主动方/被动方"的关系,不是"平起平坐互相等"的关系,所以不适合。 --- ## 🧠 记忆口诀 > **"Latch 是倒计时,主等子或子等主;Barrier 是栅栏,大家一起过才算数。"** - **CountDownLatch** = 发令枪:枪响(countDown)→ 大家跑(await 结束) - **CyclicBarrier** = 集合点:人到齐了 → 一起走 你的两个场景都是 **"发令枪"** 模式,选 `CountDownLatch` 就对了。 --- # 3-给我一个 CyclicBarrier(循环栅栏)的业务场景说明和代码示例 # CyclicBarrier 业务场景:电商大促三阶段联合作战 ## 🎯 场景描述 双 11 前夕,电商平台有 **5 个团队**需要分阶段协同作战: | 阶段 | 任务内容 | 协作要求 | |-----|---------|---------| | 第一阶段 | 各团队独立准备(备货、扩容、培训等) | 5 个团队**全部准备完毕**后,才能进入演练 | | 第二阶段 | 全链路联合压测演练 | 5 个团队**全部演练通过**后,才能正式上线 | | 第三阶段 | 开启大促,实时监控 | 5 个团队**全部就位**后,统一开卖 | **关键点**:每个阶段都必须等所有人到齐,才能进入下一阶段 —— 这正是 **CyclicBarrier** 的用武之地(可循环复用,正好对应三个阶段的重复等待)。 --- ## 💻 完整代码示例 ```java import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ThreadLocalRandom; public class Double11Preparation { // 5 个团队,每个阶段到齐后执行"老板宣布" private static final CyclicBarrier barrier = new CyclicBarrier(5, () -> { System.out.println(">>> 【屏障触发】所有团队已就位,进入下一阶段! "); }); public static void main(String[] args) { String[] teams = {"库存团队", "营销团队", "技术团队", "客服团队", "物流团队"}; for (String team : teams) { new Thread(new TeamWorker(team), team).start(); } } static class TeamWorker implements Runnable { private final String teamName; TeamWorker(String teamName) { this.teamName = teamName; } @Override public void run() { try { // ========== 第一阶段:准备 ========== doPhase("准备阶段", "备货/扩容/培训"); barrier.await(); // 等所有人准备完 // ========== 第二阶段:演练 ========== doPhase("演练阶段", "全链路压测"); barrier.await(); // 等所有人演练完 // ========== 第三阶段:上线 ========== doPhase("上线阶段", "开启大促监控"); barrier.await(); // 等所有人就位 System.out.println("🎉 " + teamName + ":大促正式开始,全力以赴!"); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } } private void doPhase(String phaseName, String task) throws InterruptedException { int cost = ThreadLocalRandom.current().nextInt(1000, 3000); System.out.println(teamName + " 正在进行【" + phaseName + "】:" + task + "..."); Thread.sleep(cost); System.out.println(teamName + " 完成【" + phaseName + "】,耗时 " + cost + "ms,等待其他团队..."); } } } ``` --- ## 📋 运行结果示例 ``` 营销团队 正在进行【准备阶段】:备货/扩容/培训... 客服团队 正在进行【准备阶段】:备货/扩容/培训... 物流团队 正在进行【准备阶段】:备货/扩容/培训... 库存团队 正在进行【准备阶段】:备货/扩容/培训... 技术团队 正在进行【准备阶段】:备货/扩容/培训... 营销团队 完成【准备阶段】,耗时 1243ms,等待其他团队... 客服团队 完成【准备阶段】,耗时 1567ms,等待其他团队... 库存团队 完成【准备阶段】,耗时 1890ms,等待其他团队... 物流团队 完成【准备阶段】,耗时 2103ms,等待其他团队... 技术团队 完成【准备阶段】,耗时 2456ms,等待其他团队... >>> 【屏障触发】所有团队已就位,进入下一阶段! 技术团队 正在进行【演练阶段】:全链路压测... 营销团队 正在进行【演练阶段】:全链路压测... ... >>> 【屏障触发】所有团队已就位,进入下一阶段! ... >>> 【屏障触发】所有团队已就位,进入下一阶段! 🎉 库存团队:大促正式开始,全力以赴! 🎉 营销团队:大促正式开始,全力以赴! ... ``` --- ## 🔑 核心要点图解 ``` 阶段一:准备 阶段二:演练 阶段三:上线 库存 ████ 库存 ████ 库存 ████ 营销 ███ 营销 █████ 营销 ███ 技术 █████ 技术 ███ 技术 █████ ← 各自速度不同 客服 ████ 客服 ██████ 客服 ███ 物流 ██████ 物流 ████ 物流 █████ ↓ ↓ ↓ [屏障等待] [屏障等待] [屏障等待] ↓ ↓ ↓ 全部到齐! 全部到齐! 全部到齐! 进入阶段二 进入阶段三 大促开启! ``` --- ## ⚠️ 与 CountDownLatch 的关键区别 | | 本场景用 CyclicBarrier ✅ | 如果用 CountDownLatch ❌ | |---|---|---| | 复用性 | 一个实例直接复用 3 次 | 每阶段都要新建一个实例 | | 角色关系 | 5 个团队**平级互相等待** | 像"老板等员工",单向等待 | | 屏障动作 | 支持自动执行阶段切换通知 | 需额外写通知逻辑 | | 代码量 | 简洁 | 繁琐 | --- ## 🧠 一句话记忆 > **CyclicBarrier = 马拉松起跑线**:所有选手(线程)到齐后,发令枪响,大家一起冲向下一个阶段;下一轮比赛还能接着用这根起跑线。 ---