Java线程同步问题求助:标签通信与计算线程同步实现
多线程同步问题修复方案
你当前的代码存在几个核心问题导致同步失效,以下是问题分析和对应的修复方案:
问题分析
- wait/notify未绑定同步锁:
wait()和notify()必须在持有同一个锁对象的synchronized块/方法中调用,否则会抛出IllegalMonitorStateException,且不同锁的通知无法唤醒其他线程的等待。 - 缺少全局线程完成计数:单个计算线程完成后就调用
notify(),会导致主线程提前被唤醒,无法保证等待所有计算任务完成。 - 未处理wait虚假唤醒:
wait()可能在没有收到notify()的情况下被唤醒,必须循环判断等待条件,不能用单次if判断。
修复方案
方案1:使用CountDownLatch(推荐,简洁可靠)
CountDownLatch是JUC包专门为"等待多线程完成"场景设计的工具,初始化时设置任务总数,每个线程完成后调用countDown(),主线程调用await()等待计数归0。
主线程代码修改:
import java.util.concurrent.CountDownLatch; public void f() { // ... 前置逻辑 CountDownLatch latch = new CountDownLatch(dataObject.size()); // 初始计数为任务总数 for (byte[] data : dataObject) { byte[] response = tag.transive(data); // 与标签通信 CalculationThread calc = new CalculationThread(data, card, latch); calc.start(); } try { latch.await(); // 阻塞等待所有计算线程完成 } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 恢复线程中断状态 return; } // ... 计算完成后的后续逻辑 }
CalculationThread代码修改:
import java.util.concurrent.CountDownLatch; public class CalculationThread extends Thread { private byte[] data; private Card card; private CountDownLatch latch; // 构造方法新增latch参数 public CalculationThread(byte[] data, Card card, CountDownLatch latch) { this.data = data; this.card = card; this.latch = latch; } @Override public void run() { try { // ... 你的计算逻辑 } finally { latch.countDown(); // 无论计算成功/失败,都减少计数 } } }
方案2:用synchronized+计数器(手动实现wait/notify)
如果坚持使用wait/notify,需要通过共享锁对象和计数器来跟踪所有线程的完成状态:
先修改Card类,增加计数相关逻辑:
public class Card { private boolean ready; private int completedTasks = 0; private int totalTasks = 0; // 同步方法:递增完成任务数并通知等待线程 public synchronized void incrementCompleted() { completedTasks++; notifyAll(); // 通知所有等待的线程 } public synchronized void setTotalTasks(int total) { this.totalTasks = total; } // 判断是否所有任务都完成 public synchronized boolean isAllCompleted() { return completedTasks >= totalTasks; } // 原有getter/setter... }
主线程代码修改:
public void f() { // ... 前置逻辑 card.setTotalTasks(dataObject.size()); // 设置总任务数 for (byte[] data : dataObject) { byte[] response = tag.transive(data); // 与标签通信 CalculationThread calc = new CalculationThread(data, card); calc.start(); } synchronized (card) { // 必须持有card对象的锁 // 循环判断+wait,处理虚假唤醒 while (!card.isAllCompleted()) { try { card.wait(); // 释放锁并等待通知 } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } } } // ... 计算完成后的后续逻辑 }
CalculationThread代码修改:
public class CalculationThread extends Thread { private byte[] data; private Card card; public CalculationThread(byte[] data, Card card) { this.data = data; this.card = card; } @Override public void run() { try { // ... 你的计算逻辑 } finally { card.incrementCompleted(); // 完成后通知主线程 } } }
关键注意事项
- 使用
CountDownLatch时,务必在finally块中调用countDown(),避免计算抛出异常导致主线程永久阻塞。 - 使用
wait/notify时,必须在synchronized块中执行,且用while循环判断等待条件,绝对不能用if。 - 所有线程必须绑定同一个锁对象(比如上述方案中的
card),否则通知无法跨线程生效。
内容的提问来源于stack exchange,提问作者skynet
相关产品推荐
相关产品推荐

