拓冰建站拓冰建站
首页 / 资讯中心 / 正文

理解多线程问题(银行取钱为例)

深入理解Java多线程从银行取钱案例解析线程安全问题在并发编程的世界里线程安全是一道绕不开的坎。就像银行取钱时若多个用户同时操作同一账户稍不注意就会出现“余额超扣”“重复扣款”等问题。今天我们就以银行取钱为场景聊聊Java多线程中的线程安全与同步机制。一、场景还原为什么会出现线程安全问题假设某银行账户初始余额为1000元有两个用户同时尝试取钱每次取200元各取5次。如果没有任何“锁”的保护会发生什么用户A判断余额≥200准备扣款此时CPU切换到用户B用户B也判断余额≥200准备扣款最终两个用户都“成功”取走200元但账户实际只扣了200元出现数据不一致即线程安全问题。二、代码实现模拟银行取钱的线程安全问题与解决我们通过三个类来模拟这个场景 BankAccount 银行账户、 WithdrawThread 取钱线程、 BankTest 测试类。1. 银行账户类BankAccountpublic class BankAccount { public double balance 1000; // 初始余额1000元 }2. 取钱线程类WithdrawThreadpublic class WithdrawThread implements Runnable { public BankAccount account; public WithdrawThread(BankAccount account) { this.account account; } public void run() { for (int i 0; i 5; i) { // 模拟取5次钱 try { Thread.sleep(10); // 模拟取钱耗时放大并发问题 } catch (InterruptedException e) { throw new RuntimeException(e); } // 同步代码块通过“对象锁”保证操作原子性 synchronized (this) { if (account.balance 200) { // 每次取200元 System.out.println(Thread.currentThread().getName() 取出200元剩余余额 (account.balance - 200)); account.balance - 200; } else { System.out.println(Thread.currentThread().getName() 取钱失败余额不足); } } } } }3. 测试类BankTestpublic class BankTest { public static void main(String[] args) { BankAccount account new BankAccount(); WithdrawThread wt new WithdrawThread(account); // 启动两个线程模拟两个用户同时取钱 new Thread(wt, 用户1).start(); new Thread(wt, 用户2).start(); } }三、核心原理synchronized同步机制在 WithdrawThread 的 run 方法中我们使用了 synchronized (this) 来保护取钱逻辑。它的核心作用是给这段代码加一把“锁”同一时间只有一个线程能执行被锁住的代码块。锁的对象这里用的是 this 即 WithdrawThread 的实例对象确保两个线程竞争同一把锁。原子性保证“判断余额是否充足”和“扣款”这两个操作被合并为一个“原子操作”不会被其他线程打断。四、运行结果与分析当我们运行 BankTest 后会看到类似如下的输出:用户1 取出200元剩余余额800.0用户2 取出200元剩余余额600.0用户1 取出200元剩余余额400.0用户2 取出200元剩余余额200.0用户1 取出200元剩余余额0.0用户2 取钱失败余额不足用户1 取钱失败余额不足用户2 取钱失败余额不足…可以看到余额从1000元依次递减最终正常耗尽没有出现“超扣”或“重复扣”的情况——这就是线程安全的体现。五、总结线程安全问题的本质是多线程对共享资源的并发操作导致数据不一致。在银行取钱场景中我们通过 synchronized 同步代码块给共享资源账户余额的操作加上“保护锁”从而保证了并发环境下的数据一致性。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门