多线程总结五:线程通信(一)
当线程在程序中运行时,线程的调度具有一定的透明性,程序通常无法准确控制线程的轮换执行,Java提供了一些机制来保证线程协调运行。
1、传统的线程通信借助Object类提供的wait()、notify()和notifyAll()三个方法,这三个方法必须由同步监视器对象来调用。
a、wait():导致当前线程等待,直到其他线程调用该同步监视器的notify()方法或notifyAll()方法来唤醒该线程;
b、notify():唤醒在此同步监视器上等待的单个线程;
c、notifyAll():唤醒在此同步监视器上等待的所有线程;
2、模拟情景:系统中有两个线程,分别代表存款者和取款者。系统要求存款者和取款者不断重复存款和取款的动作,而且要求每当存款者将钱存入指定账户后,取款者就立即取出该笔钱。不允许存款者连续两次存钱,也不允许取钱者连续两次取钱。
1 /** 2 * @Title: Account3.java 3 * @Package 4 * @author 任伟 5 * @date 2014-12-8 下午6:50:34 6 * @version V1.0 7 */ 8 9 /** 10 * @ClassName: Account3 11 * @Description: 线程安全Account类,提供存钱和取钱的操作 12 * @author 任伟 13 * @date 2014-12-8 下午6:50:34 14 */ 15 public class Account3 { 16 private String accountNo; // 账户编号 17 private double balance; // 账户余额 18 private boolean flag = false; // 表示账户中是否已有存款的骑标 19 20 public String getAccountNo() { 21 return accountNo; 22 } 23 24 public void setAccountNo(String accountNo) { 25 this.accountNo = accountNo; 26 } 27 28 public double getBalance() { 29 return balance; 30 } 31 32 public Account3() { 33 super(); 34 } 35 36 public Account3(String accountNo, double balance) { 37 super(); 38 this.accountNo = accountNo; 39 this.balance = balance; 40 } 41 42 public boolean equals(Object anObject) { 43 if (this == anObject) 44 return true; 45 if (anObject != null && anObject.getClass() == Account.class) { 46 Account target = (Account) anObject; 47 return target.getAccountNo().equals(accountNo); 48 } 49 return false; 50 } 51 52 public int hashCode() { 53 return accountNo.hashCode(); 54 } 55 56 // 取钱操作 57 public synchronized void draw(double drawAmount) { 58 try { 59 if (!flag) {//如果flag为假,表明账户中还没有人存钱进去,取钱方法阻塞 60 wait(); 61 } else {//否则就进行取钱操作 62 if (balance >= drawAmount) { 63 System.out.println(Thread.currentThread().getName() 64 + "取钱成功,吐出钞票:" + drawAmount); 65 try { 66 Thread.sleep(1); 67 } catch (InterruptedException e) { 68 e.printStackTrace(); 69 } 70 balance -= drawAmount; 71 System.out.println("余额为:" + balance); 72 } else { 73 System.out.println(Thread.currentThread().getName() 74 + "取钱失败!余额不足!"); 75 } 76 //将账户是否已有存款的旗标设为false 77 flag = false; 78 //唤醒其他进程 79 notifyAll(); 80 } 81 } catch (Exception e) { 82 e.printStackTrace(); 83 } 84 } 85 86 //存钱操作 87 public synchronized void deposit(double depositAmount){ 88 try{ 89 if(flag){//如果flag为真,表明账户中已有人存钱进去,存钱方法阻塞 90 wait(); 91 }else{ 92 //执行存款操作 93 System.out.println(Thread.currentThread().getName()+" 存款:" +depositAmount); 94 balance += depositAmount; 95 System.out.println("账户余额为:" + balance); 96 //将账户是否已有存款的旗标设为true 97 flag=true; 98 notifyAll(); 99 100 } 101 }catch (Exception e) { 102 e.printStackTrace(); 103 } 104 } 105 }
1 /** 2 * @Title: DrawDepositTest3.java 3 * @Package 4 * @author 任伟 5 * @date 2014-12-9 下午2:09:38 6 * @version V1.0 7 */ 8 9 /** 10 * @ClassName: DrawDepositTest3 11 * @Description: 测试存款和取款线程 12 * @author 任伟 13 * @date 2014-12-9 下午2:09:38 14 */ 15 public class DrawDepositTest3 { 16 public static void main(String[] args) { 17 Account3 acct = new Account3("1234567", 0); 18 new DrawThread3("取钱者", acct, 800).start(); 19 new DepositThread3("存钱者甲", acct, 800).start(); 20 new DepositThread3("存钱者乙", acct, 800).start(); 21 new DepositThread3("存钱者丙", acct, 800).start(); 22 } 23 } 24 25 //取款线程 26 class DrawThread3 extends Thread{ 27 private Account3 account; //模拟用户账户 28 private double drawAmount; //取钱数 29 30 public DrawThread3(String name, Account3 account, double drawAmount) { 31 super(name); 32 this.account = account; 33 this.drawAmount = drawAmount; 34 } 35 36 //重复100次执行取钱操作 37 public void run(){ 38 for(int i=0; i<100; i++){ 39 account.draw(drawAmount); 40 } 41 } 42 } 43 44 //春款线程 45 class DepositThread3 extends Thread{ 46 private Account3 account; //模拟用户账户 47 private double depositAmount; //存钱数 48 49 public DepositThread3(String name, Account3 account, double depositAmount) { 50 super(name); 51 this.account = account; 52 this.depositAmount = depositAmount; 53 } 54 55 //重复100次执行存钱操作 56 public void run(){ 57 for(int i=0; i<100; i++){ 58 account.deposit(depositAmount); 59 } 60 } 61 62 63 }
Result
如图所示的阻塞并不是死锁,取钱者线程已经执行结束,而存款者线程只是在等待其他线程来取钱而已,并不是等待其他线程释放同步监视器。