看一个例子,背景是,银行卡里有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