澳五机器人 Java 多线程开发系列之七:玩转多线程(线程的协作)

admin2个月前澳五机器人24

结合你之前关注的Java多线程并发编程中store/load与RMW操作、工业级高并发场景落地的相关背景,

这篇内容从最基础的原生等待通知机制讲起,逐步延伸到JUC高阶同步工具的实战用法,把线程之间从

“各自独立运行”到“精准协同配合”的完整知识体系讲透,完全避开零散API堆砌的问题,所有案例

都贴合真实生产环境的使用场景。


一、线程协作的核心本质


之前我们接触的多线程代码,大多是多个线程各自独立执行任务,互相之间没有任何交互,但真实的高

并发业务场景里,绝大多数任务都需要多个线程配合完成:比如经典的生产者-消费者场景,生产线程往

队列里放数据,消费线程从队列里取数据处理,两个线程的执行节奏必须精准协同,不能出现消费线程

空转浪费CPU,也不能出现生产线程往满队列里重复丢数据的问题,这就是线程协作要解决的核心问题。


二、最基础的原生协作:wait() / notify() 等待通知机制


这是Java语言从JDK1.0就内置的最基础线程协作能力,所有对象都天然自带这套机制,核心规则非常清晰:


必须在synchronized同步块内部调用wait()和notify()方法,否则会直接抛出IllegalMonitorStateException

异常,因为这两个方法的执行前提是线程已经拿到了对象的监视器锁。

线程调用obj.wait()之后,会立刻释放持有的对象锁,进入该对象的等待队列,直到其他线程调用

obj.notify()/notifyAll(),才会从等待队列里被唤醒,重新去竞争对象锁,拿到锁之后才能从wait()方

法处继续向下执行。

生产级代码里永远不要用if条件判断直接包裹wait(),必须用while循环做条件二次校验,避免线程被虚假唤

醒后直接绕过条件判断,引发队列溢出的生产故障。

标准生产者-消费者基础写法示例:

java

// 共享任务队列

Queue<String> taskQueue = new LinkedList<>();

int MAX_SIZE = 10;


// 生产者线程

new Thread(() -> {

    synchronized (taskQueue) {

        while (taskQueue.size() == MAX_SIZE) {

            taskQueue.wait(); // 队列满就等待,释放锁

        }

        taskQueue.add(UUID.randomUUID().toString());

        taskQueue.notifyAll(); // 唤醒所有等待的消费线程

    }

}).start();


// 消费者线程

new Thread(() -> {

    synchronized (taskQueue) {

        while (taskQueue.isEmpty()) {

            taskQueue.wait(); // 队列为空就等待

        }

        String task = taskQueue.poll();

        taskQueue.notifyAll(); // 唤醒生产者线程

    }

}).start();


三、JUC高阶协作工具类实战


从JDK1.5开始,java.util.concurrent包提供了一系列专为复杂线程协作设计的高阶工具,完全可以替代原生等

待通知的手写逻辑,代码更简洁、性能更稳定:


CountDownLatch 倒数计数器‌:适用于“一个主线程等待N个子线程全部执行完成再继续”的场景,比如并行

拉取多个远程接口的数据,所有接口返回之后再做结果聚合。主线程调用await()进入阻塞,每个子线程执行完

成后调用countDown()把计数器减1,计数器归0时主线程自动被唤醒,比手写wait/notify逻辑简洁10倍。

CyclicBarrier 循环屏障‌:适用于“N个线程互相等待,所有线程都到达屏障点之后,再同时继续向下执行”的

场景,比如多线程并行计算大文件分片,所有分片计算完成之后,再统一启动结果合并线程。和CountDownLatch

最大的区别是它可以循环复用,计数器归0之后自动重置,适合重复执行的批量任务场景。

Semaphore 信号量‌:适用于“控制同时访问共享资源的线程数量”的协作场景,比如数据库连接池最多允许10个

线程同时获取连接,超过的线程就阻塞等待,本质上就是一个带许可计数的流量控制器。

Exchanger 双线程交换器‌:专为两个线程的数据交换场景设计,两个线程各自执行到exchange()方法时会互相等

待,双方都到达之后就可以交换各自传入的数据,非常适合并行数据校验场景,两个线程分别从两个数据源读取同

一份数据,交换之后做一致性比对。

四、生产级避坑要点

永远不要用notify(),优先用notifyAll(),避免出现“通知丢失”的问题:如果多个线程在同一个对象上等待,

notify()只会随机唤醒一个线程,很可能唤醒的不是当前最需要被唤醒的线程,导致部分线程永远卡在等待队列里。

高并发场景下优先用JUC工具类替代手写wait/notify逻辑,JUC的协作工具底层基于AQS队列实现,比原生监

视器的唤醒效率高30%以上,不会出现大量线程唤醒后无效竞争锁的“惊群效应”。

不要在业务代码里直接调用Thread类的stop()/suspend()方法强制中断协作线程,很容易导致共享对象的锁永

远无法释放,引发整个服务的死锁故障,优先用中断标记位+响应中断的方式优雅终止协作流程。


需要我为你生成‌CyclicBarrier多线程并行计算大文件分片的完整可运行实战代码‌,覆盖分片读取、并行计算、结果

聚合全流程,直接就能用于生产场景参考吗?


澳五机器人 澳八机器人 河内机器人 加拿大机器人 花开月下机器人 朱雀机器人 速飞机器人 名爵机器人 飞天机器人 BV机器人 涂六飞单机器人 美猴王机器人 大富豪机器人 速讯机器人 五球助手 十球助手

相关文章

AI时代,重温10大经典排序算法(一)

在人工智能技术重塑编程生态的当下,重温经典排序算法不仅是夯实计算机科学基础的必经之路,更能帮助开发者理解AI工具优化算法的底层逻辑。作为数据处理的核心基石,排序算法广泛应用于搜索引擎、推荐系统、数据分...

澳五机器人 从零开始落地自动化测试全流程落地方案

结合你之前深度探索的AI辅助测试体系建设、UI自动化全流程Agent Skill串联、Playwright自动化失败自愈提效的相关实践背景,从零推进自动化测试绝对不能一上来就盲目铺工具、写脚本,必须遵...

Claude Code 使用指南(六):企业级定制与生态扩展

引言:从标准化到定制化在前五篇指南中,我们系统介绍了 Claude Code 的基础使用、团队协作和企业级部署。本篇将聚焦企业级定制化需求,深入探讨如何通过扩展机制、模型微调和生态集成,使 Claud...

NET 10 新功能新增功能介绍:WebSocket 功能增强(二)

在 .NET 10 的持续演进中,WebSocket 功能得到了进一步的增强,为开发者提供了更高效、更灵活和更安全的实时通信解决方案。这些改进不仅提升了 WebSocket 的性能和易用性,还加强了与...

Visual Studio 四月更新 —— 云代理集成 澳五机器人

以下是为您翻译的内容:GitHub Copilot 在 Visual Studio 中的智能化能力正随每次发布持续升级。本次更新将云代理集成置于核心位置,让开发者无需离开 IDE 即可开启远程编码会话...

澳五机器人 如何基于Koog构建生产级多模型容灾方案

如何基于Koog构建生产级多模型容灾方案基于 Koog 构建生产级多模型容灾方案依托 Koog 原生的 MultiLLMPromptExecutor 多模型执行器抽象,结合断路器、健康探测、智能路由三...