模擬銀行 ATM 系統核心業務:多線程並發存款、取款、查詢餘額。通過 synchronized 關鍵字實現線程安全,演示線程生命周期、狀態轉換、同步機制等核心概念。
public class Account {
private final String name; // 用戶名
private int balance; // 餘額(非 volatile,靠 synchronized 保證可見性)
public Account(String name, int initialBalance) {
this.name = name;
this.balance = initialBalance;
}
/** 存款:線程安全 */
public synchronized void deposit(int amount) {
if (amount <= 0) throw new IllegalArgumentException("存款金額必須大於 0");
int prev = balance;
balance += amount;
log("存款", prev, amount, balance);
}
/** 取款:線程安全,餘額不足時取走剩餘所有 */
public synchronized int withdraw(int amount) {
if (amount <= 0) throw new IllegalArgumentException("取款金額必須大於 0");
int prev = balance;
int actual = Math.min(amount, balance); // 餘額不足全取
balance -= actual;
log("取款", prev, actual, balance);
return actual;
}
/** 查詢餘額:線程安全 */
public synchronized int getBalance() {
return balance;
}
public String getName() { return name; }
private void log(String op, int prev, int amt, int after) {
System.out.printf("[%s] %s: 戶主=%s, 操作前=%d, 金額=%d, 操作後=%d%n",
Thread.currentThread().getName(), op, name, prev, amt, after);
}
}
public class DepositTask implements Runnable {
private final Account account;
private final int amount;
public DepositTask(Account account, int amount) {
this.account = account;
this.amount = amount;
}
@Override
public void run() {
// 模擬網絡/處理延遲
sleepRandom(5, 20);
account.deposit(amount);
}
private void sleepRandom(int min, int max) {
try { Thread.sleep((long) (Math.random() * (max - min + 1) + min)); }
catch (InterruptedException e) { Thread.currentThread().interrupt(); }
}
}
public class WithdrawTask implements Runnable {
private final Account account;
private final int amount;
public WithdrawTask(Account account, int amount) {
this.account = account;
this.amount = amount;
}
@Override
public void run() {
sleepRandom(5, 20);
int actual = account.withdraw(amount);
System.out.printf("[%s] 取款完成: 請求=%d, 實際到手=%d%n",
Thread.currentThread().getName(), amount, actual);
}
private void sleepRandom(int min, int max) {
try { Thread.sleep((long) (Math.random() * (max - min + 1) + min)); }
catch (InterruptedException e) { Thread.currentThread().interrupt(); }
}
}
public class AtmSimulation {
public static void main(String[] args) {
Account account = new Account("張三", 0); // 初始餘額 0
// 創建並啟動線程
Thread t1 = new Thread(new DepositTask(account, 100), "存款線程-1");
Thread t2 = new Thread(new DepositTask(account, 200), "存款線程-2");
Thread t3 = new Thread(new WithdrawTask(account, 500), "取款線程-1");
t1.start();
t2.start();
t3.start();
// 等待所有線程結束
try {
t1.join();
t2.join();
t3.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("=== 所有操作完成 ===");
System.out.printf("最終餘額: %d%n", account.getBalance());
}
}
[存款線程-1] 存款: 戶主=張三, 操作前=0, 金額=100, 操作後=100
[存款線程-2] 存款: 戶主=張三, 操作前=100, 金額=200, 操作後=300
[取款線程-1] 取款: 戶主=張三, 操作前=300, 金額=300, 操作後=0
[取款線程-1] 取款完成: 請求=500, 實際到手=300
=== 所有操作完成 ===
最終餘額: 0
| 狀態 | 觸發條件 | 代碼體現 |
|---|---|---|
| NEW | new Thread() | 創建 DepositTask 實例 |
| RUNNABLE | start() | t1.start() 後,等待 CPU 調度 |
| RUNNING | 獲得 CPU 時間片 | 執行 run() 中的業務邏輯 |
| BLOCKED | 嘗試進入 synchronized 方法但鎖被佔用 | 取款線程等待存款線程釋放 account 鎖 |
| TERMINATED | run() 正常返回或拋出未捕獲異常 | 各線程執行完畢 |
deposit/withdraw/getBalance 互斥執行,不會出現「讀取舊值 → 計算 → 寫回」被打斷導致數據不一致。Thread 就無法繼承業務基類。Runnable 解耦「任務邏輯」與「線程控制」,同一任務可提交給線程池、CompletableFuture 等不同執行器。Condition)。ExecutorService pool = Executors.newFixedThreadPool(10); pool.submit(task); 避免頻繁創建銷毀線程開銷。AtomicInteger 無鎖 CAS 效能更高。本例通過 Account 封裝共享狀態,synchronized 實例方法保護臨界區,Runnable 任務提交給線程執行,完整演示了 Java 多線程並發控制的基礎模式。理解「共享可變狀態 + 互斥同步」是並發編程的入門鑰匙,進階學習請關注 java.util.concurrent 包下的高級組件。