You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java线程同步问题求助:标签通信与计算线程同步实现

多线程同步问题修复方案

你当前的代码存在几个核心问题导致同步失效,以下是问题分析和对应的修复方案:


问题分析

  1. wait/notify未绑定同步锁:wait()和notify()必须在持有同一个锁对象的synchronized块/方法中调用,否则会抛出IllegalMonitorStateException,且不同锁的通知无法唤醒其他线程的等待。
  2. 缺少全局线程完成计数:单个计算线程完成后就调用notify(),会导致主线程提前被唤醒,无法保证等待所有计算任务完成。
  3. 未处理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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 03:31:22