那存钱取钱为例:
要求实现一次存一次取的操作 不可出现连续存或连续取;
如果只有存钱和取钱各自只有一个线程在操作使用 if 的话可以满足要求:
1 package com.thread; 2 /** 3 * 模拟同步取款的问题 4 * @author dr 5 * 6 */ 7 public class ThreadTest { 8 public static void main(String[] args) { 9 final Account account = new Account(); 10 //取出200 11 new Thread(new Runnable() { 12 @Override 13 public void run() { 14 for(int i=0;i<3;i++){ 15 try { 16 Thread.sleep(500); 17 } catch (InterruptedException e) { 18 // TODO Auto-generated catch block 19 e.printStackTrace(); 20 } 21 account.getMoney(200); 22 } 23 } 24 }).start(); 25 //存入300 26 new Thread(new Runnable() { 27 @Override 28 public void run() { 29 for(int i=0;i<3;i++){ 30 try { 31 Thread.sleep(500); 32 } catch (InterruptedException e) { 33 // TODO Auto-generated catch block 34 e.printStackTrace(); 35 } 36 account.setMoney(300); 37 } 38 } 39 }).start(); 40 } 41 } 42 class Account { 43 44 private int balance = 1000; 45 private boolean setMoney = true; 46 public synchronized void getMoney(int count){ 47 if(setMoney){ 48 try { 49 this.wait(); 50 } catch (InterruptedException e) { 51 // TODO Auto-generated catch block 52 e.printStackTrace(); 53 } 54 } 55 int result =balance - count; 56 if(result >= 0){ 57 balance = result; 58 System.out.println(Thread.currentThread().getName()+"取出:"+count+"元,剩余:"+balance); 59 }else{ 60 System.out.println("余额不足..."); 61 } 62 setMoney = true; 63 this.notify(); 64 } 65 public synchronized void setMoney(int count){ 66 if(!setMoney){ 67 try { 68 this.wait(); 69 } catch (InterruptedException e) { 70 // TODO Auto-generated catch block 71 e.printStackTrace(); 72 } 73 } 74 balance += count; 75 System.out.println(Thread.currentThread().getName()+"存入:"+count+"元,剩余:"+balance); 76 setMoney = false; 77 this.notify(); 78 } 79 80 }
但是如果存钱和取钱包含多个线程的话 if 就不行 只有使用while才能满足条件
1 package com.thread; 2 /** 3 * 模拟同步取款的问题 4 * @author dr 5 * 6 */ 7 public class ThreadTest { 8 public static void main(String[] args) { 9 final Account account = new Account(); 10 //取出200 两个取钱的线程 11 for(int i=0;i<2;i++){ 12 new Thread(new Runnable() { 13 @Override 14 public void run() { 15 for(int i=0;i<3;i++){ 16 try { 17 Thread.sleep(500); 18 } catch (InterruptedException e) { 19 // TODO Auto-generated catch block 20 e.printStackTrace(); 21 } 22 account.getMoney(200); 23 } 24 } 25 }).start(); 26 } 27 //存入300 两个存钱的线程 28 for(int i=0;i<2;i++){ 29 new Thread(new Runnable() { 30 @Override 31 public void run() { 32 for(int i=0;i<3;i++){ 33 try { 34 Thread.sleep(500); 35 } catch (InterruptedException e) { 36 // TODO Auto-generated catch block 37 e.printStackTrace(); 38 } 39 account.setMoney(300); 40 } 41 } 42 }).start(); 43 } 44 } 45 } 46 class Account { 47 48 private int balance = 1000; 49 //先存钱 50 private boolean setMoney = true; 51 public synchronized void getMoney(int count){ 52 while(setMoney){ 53 try { 54 this.wait(); 55 } catch (InterruptedException e) { 56 // TODO Auto-generated catch block 57 e.printStackTrace(); 58 } 59 } 60 int result =balance - count; 61 if(result >= 0){ 62 balance = result; 63 System.out.println(Thread.currentThread().getName()+"取出:"+count+"元,剩余:"+balance); 64 }else{ 65 System.out.println("余额不足..."); 66 } 67 setMoney = true; 68 this.notifyAll(); 69 } 70 public synchronized void setMoney(int count){ 71 while(!setMoney){ 72 try { 73 this.wait(); 74 } catch (InterruptedException e) { 75 // TODO Auto-generated catch block 76 e.printStackTrace(); 77 } 78 } 79 balance += count; 80 System.out.println(Thread.currentThread().getName()+"存入:"+count+"元,剩余:"+balance); 81 setMoney = false; 82 this.notifyAll(); 83 } 84 85 }
分析:
有A、B、C、D四个线程 AB存钱线程,CD取钱线程,使用if的时候,假设A执行,看标志无需wait 执行完成后 改标志为 取 ,A B 都先获得执行权 但状态不符合,
处于等待状态 A B 无执行权, C获得执行权后 执行完成后 更改状态为存 同时唤醒 A ,D获得执行权也处于等待状态。
现在只有A有执行权 A执行完成后 更改标志 先唤醒 B,B此时无需检查标志了紧接执行存款 从而导致 出现连续两次 取款的情形
使用while的时候 ,虽然B被唤醒 但经while(flag) 又会 检查标志 使其处于等待状态 使用while 要使用notifyAll 否则会出现全部等待状态
时间: 2024-10-14 01:51:17