看一个例子,背景是,银行卡里有1000块钱,在柜台取800块钱,在提款机取800块钱,理论上来说,这个是不允许的
看看实现代码
package test;
public class FetchMoney {
public static void main(String[] args) {
Bank bank = new Bank();
MoneyThread thread1 = new MoneyThread(bank); // 柜台手续线程
MoneyThread thread2 = new MoneyThread(bank); // 自助机手续线程
thread1.start();
thread2.start();
}
}
class Bank {
private int money = 1000;
public int getMoney(int money) {
if(money < 0) {
return -1;
}
if(money > this.money) {
return -2;
}
try {
Thread.sleep(1000); // 取钱前的一些准备工作
} catch (Exception e) {
e.printStackTrace();
}
this.money -= money;
return money;
}
}
class MoneyThread extends Thread {
private Bank bank;
public MoneyThread(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
System.out.println(bank.getMoney(800));
}
}
执行的结果是
800
800
Process finished with exit code 0
1000块钱的余额居然真的取出2次800块钱了
问题的根源在于,2个线程会同时访问一个实例的某个成员变量,2条线程判断条件时成员变量的值均可能是还没取钱时候的值
要解决这个问题,我们可以这么想,取钱的时候(即调getMoney方法的时候),只允许1条线程访问,就解决了
代码如下:
package test;
public class FetchMoney {
public static void main(String[] args) {
Bank bank = new Bank();
MoneyThread thread1 = new MoneyThread(bank); // 柜台手续线程
MoneyThread thread2 = new MoneyThread(bank); // 自助机手续线程
thread1.start();
thread2.start();
}
}
class Bank {
private int money = 1000;
public synchronized int getMoney(int money) {
if(money < 0) {
return -1;
}
if(money > this.money) {
return -2;
}
try {
Thread.sleep(1000); // 取钱前的一些准备工作
} catch (Exception e) {
e.printStackTrace();
}
this.money -= money;
return money;
}
public int getMoney2(int money) {
synchronized(this) {
if (money < 0) {
return -1;
}
if (money > this.money) {
return -2;
}
try {
Thread.sleep(1000); // 取钱前的一些准备工作
} catch (Exception e) {
e.printStackTrace();
}
this.money -= money;
}
return money;
}
}
class MoneyThread extends Thread {
private Bank bank;
public MoneyThread(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
System.out.println(bank.getMoney(800));
}
}
调用getMoney或者getMoney2都可以
结果为:
800
-2
Process finished with exit code 0