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

