Java 多线程银行账户操作:竞态条件与同步解决方案
以下程序模拟了一个银行账户,两个线程分别进行存款和取款操作。运行结果可能不一定,因为该程序存在竞态条件。
public class BankAccount {
private int balance;
public BankAccount(int initialBalance) {
balance = initialBalance;
}
public synchronized void deposit(int amount) {
balance += amount;
}
public synchronized void withdraw(int amount) {
balance -= amount;
}
public synchronized int getBalance() {
return balance;
}
}
public class Accountant implements Runnable {
private BankAccount account;
public Accountant(BankAccount account) {
this.account = account;
}
public void run() {
for (int i = 0; i < 10000; i++) {
account.deposit(1);
}
}
}
public class Cashier implements Runnable {
private BankAccount account;
public Cashier(BankAccount account) {
this.account = account;
}
public void run() {
for (int i = 0; i < 10000; i++) {
account.withdraw(1);
}
}
}
public class Bank {
public static void main(String[] args) {
BankAccount account = new BankAccount(10000);
Thread t1 = new Thread(new Accountant(account));
Thread t2 = new Thread(new Cashier(account));
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println('Final balance: ' + account.getBalance());
}
}
在多线程环境下,两个线程同时访问同一个账户对象,一个线程在存钱,一个线程在取钱,可能会导致账户余额出现错误的情况。例如,如果账户余额为10000,线程1存入1元,线程2取出1元,如果两个操作不是原子操作,可能会出现线程1存入1元后,线程2还未来得及取出1元,此时账户余额为10001,但是线程2取出1元后,账户余额变为9999,导致账户余额出现错误。
因此,需要对存钱和取钱的方法进行同步,保证每次只有一个线程访问该对象,避免竞态条件的发生。在本例中,我们使用 synchronized 关键字对 deposit、withdraw 和 getBalance 方法进行同步,保证了每次只有一个线程可以访问账户对象,从而避免了竞态条件的发生。
代码解读:
BankAccount类代表银行账户,包含余额balance和存款deposit、取款withdraw、获取余额getBalance等方法。Accountant类代表存款线程,负责向账户存款。Cashier类代表取款线程,负责从账户取款。Bank类为主程序,创建账户、线程并启动线程。
关键点:
- 竞态条件:多个线程同时访问共享资源,且操作结果依赖于线程执行顺序,导致最终结果不可预测的情况。
- 同步:使用
synchronized关键字对方法进行同步,保证同一时间只有一个线程可以访问该方法。 - 原子操作:不可分割的操作,要么全部执行,要么全部不执行。
总结:
多线程编程中,竞态条件是常见的错误来源,使用同步机制可以有效地解决竞态条件问题,保证程序的正确性。
原文地址: https://www.cveoy.top/t/topic/jmjj 著作权归作者所有。请勿转载和采集!