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

Java多线程共享账户同步问题求助:阻塞唤醒异常

问题分析与代码修正

原代码核心问题

  • Cuenta没必要继承Thread,它是共享资源而非执行线程,这会导致逻辑混乱
  • 同步锁混用:Persona用静态锁cerrojo,Cuenta方法用自身对象锁,两者冲突导致wait/notify逻辑完全失效
  • 存/取失败时用sleep替代wait:sleep不释放锁,其他线程根本没法操作账户,直接导致程序无响应
  • 缺少终止判断:程序会无限循环,不会在双方都无法操作时停止
  • 方法参数冗余:deposit/extract里传Cuenta实例,本身就是当前对象,完全多余

修正后的完整代码

BPA类(主类)

package BPA;

public class BPA {
    public static void main(String[] args) {
        // 初始化账户:初始余额40€,上限500€
        Cuenta laCuenta = new Cuenta(40, 500);

        Persona ramon = new Persona("Ramon", laCuenta);
        Persona quique = new Persona("Quique", laCuenta);

        ramon.start();
        quique.start();
    }
}

Persona类(线程类)

package BPA;

import java.util.Random;

public class Persona extends Thread {
    private final String nombre;
    private final Cuenta cuenta;
    private final Random random;

    public Persona(String nombre, Cuenta cuenta) {
        this.nombre = nombre;
        this.cuenta = cuenta;
        this.random = new Random();
    }

    @Override
    public void run() {
        // 只要还能进行存或取操作,就继续循环
        while (true) {
            boolean operacionRealizada = false;

            // 尝试存款(1-250€随机金额)
            int montoDeposit = random.nextInt(250) + 1;
            synchronized (cuenta) {
                operacionRealizada = cuenta.deposit(nombre, montoDeposit);
            }

            // 尝试取款(1-250€随机金额)
            int montoExtract = random.nextInt(250) + 1;
            synchronized (cuenta) {
                operacionRealizada |= cuenta.extract(nombre, montoExtract);
            }

            // 如果两次操作都没成功,唤醒其他线程后退出循环
            if (!operacionRealizada) {
                synchronized (cuenta) {
                    cuenta.notifyAll();
                }
                System.out.println(nombre + " 无法进行任何操作,退出");
                break;
            }

            // 操作成功后,唤醒其他线程并等待对方执行
            synchronized (cuenta) {
                cuenta.notifyAll();
                try {
                    cuenta.wait();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }
    }
}

Cuenta类(共享账户资源)

package BPA;

public class Cuenta {
    private int saldo;
    private final int saldoMax;
    private final int saldoMin = 1;

    public Cuenta(int saldoInicial, int saldoMax) {
        this.saldo = saldoInicial;
        this.saldoMax = saldoMax;
    }

    // 存款方法,返回是否操作成功
    public boolean deposit(String nombre, int monto) {
        if (saldo + monto <= saldoMax) {
            saldo += monto;
            System.out.println(nombre + " 存款 " + monto + "€,当前余额:" + saldo + "€");
            return true;
        } else {
            System.out.println(nombre + " 尝试存款 " + monto + "€失败,余额已达上限,当前余额:" + saldo + "€");
            return false;
        }
    }

    // 取款方法,返回是否操作成功
    public boolean extract(String nombre, int monto) {
        if (saldo - monto >= saldoMin) {
            saldo -= monto;
            System.out.println(nombre + " 取款 " + monto + "€,当前余额:" + saldo + "€");
            return true;
        } else {
            System.out.println(nombre + " 尝试取款 " + monto + "€失败,余额不足,当前余额:" + saldo + "€");
            return false;
        }
    }

    public int getSaldo() {
        return saldo;
    }
}

关键修改说明

  1. Cuenta类改造:去掉Thread继承,专注作为共享资源,deposit/extract返回操作结果,方便线程判断是否能继续执行。
  2. 同步锁统一:所有操作共享账户的逻辑都用Cuenta实例作为锁,保证线程安全,wait/notify也基于这个锁进行。
  3. 正确的等待唤醒逻辑:线程操作成功后wait让对方执行;操作失败时唤醒对方,避免死等。
  4. 终止条件判断:当线程两次操作(存+取)都失败时,说明无法继续操作,退出循环,最终所有线程退出后程序结束。
  5. 随机数优化:把Random实例放在Persona类中,避免每次创建新实例,提升效率。

内容的提问来源于stack exchange,提问作者Enrique

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 09:50:17