看一个例子,背景是,银行卡里有1000块钱,在柜台取800块钱,在提款机取800块钱,理论上来说,这个是不允许的
看看实现代码
1 package test; 2 3 public class FetchMoney { 4 5 public static void main(String[] args) { 6 7 Bank bank = new Bank(); 8 9 MoneyThread thread1 = new MoneyThread(bank); // 柜台手续线程 10 MoneyThread thread2 = new MoneyThread(bank); // 自助机手续线程 11 12 thread1.start(); 13 thread2.start(); 14 } 15 16 } 17 18 class Bank { 19 private int money = 1000; 20 21 public int getMoney(int money) { 22 23 if(money < 0) { 24 return -1; 25 } 26 27 if(money > this.money) { 28 return -2; 29 } 30 31 try { 32 Thread.sleep(1000); // 取钱前的一些准备工作 33 } catch (Exception e) { 34 e.printStackTrace(); 35 } 36 37 this.money -= money; 38 return money; 39 } 40 } 41 42 class MoneyThread extends Thread { 43 44 private Bank bank; 45 46 public MoneyThread(Bank bank) { 47 this.bank = bank; 48 } 49 50 @Override 51 public void run() { 52 System.out.println(bank.getMoney(800)); 53 } 54 }
执行的结果是
800
800
Process finished with exit code 0
1000块钱的余额居然真的取出2次800块钱了
问题的根源在于,2个线程会同时访问一个实例的某个成员变量,2条线程判断条件时成员变量的值均可能是还没取钱时候的值
要解决这个问题,我们可以这么想,取钱的时候(即调getMoney方法的时候),只允许1条线程访问,就解决了
代码如下:
1 package test; 2 3 public class FetchMoney { 4 5 public static void main(String[] args) { 6 7 Bank bank = new Bank(); 8 9 MoneyThread thread1 = new MoneyThread(bank); // 柜台手续线程 10 MoneyThread thread2 = new MoneyThread(bank); // 自助机手续线程 11 12 thread1.start(); 13 thread2.start(); 14 15 16 } 17 18 } 19 20 class Bank { 21 private int money = 1000; 22 23 public synchronized int getMoney(int money) { 24 25 if(money < 0) { 26 return -1; 27 } 28 29 if(money > this.money) { 30 return -2; 31 } 32 33 try { 34 Thread.sleep(1000); // 取钱前的一些准备工作 35 } catch (Exception e) { 36 e.printStackTrace(); 37 } 38 39 this.money -= money; 40 return money; 41 } 42 43 public int getMoney2(int money) { 44 45 synchronized(this) { 46 if (money < 0) { 47 return -1; 48 } 49 50 if (money > this.money) { 51 return -2; 52 } 53 54 try { 55 Thread.sleep(1000); // 取钱前的一些准备工作 56 } catch (Exception e) { 57 e.printStackTrace(); 58 } 59 60 this.money -= money; 61 } 62 return money; 63 } 64 } 65 66 class MoneyThread extends Thread { 67 68 private Bank bank; 69 70 public MoneyThread(Bank bank) { 71 this.bank = bank; 72 } 73 74 @Override 75 public void run() { 76 System.out.println(bank.getMoney(800)); 77 } 78 }
调用getMoney或者getMoney2都可以
结果为:
800
-2
Process finished with exit code 0