线程间通讯:多个线程在处理同一资源,但是任务不同
练习一:双线程出现线程安全问题,需要使用同步,思考同步代码添加位置
需求:银行账户存钱,显示谁在账户存钱了,存了多少钱
分析:
操作同一银行账户
两个不同的操作,一个是存,一个是显示
这个两个操作可以同时执行
代码:
1 class Bank 2 { 3 String name;//存钱人名 4 int amount; 5 6 //无论是存还是取应该都是银行的动作 7 void put(String name,int amount) 8 { 9 this.name=name; 10 this.amount=amount; 11 } 12 13 void take() 14 { 15 System.out.println(this.name+"---"+this.amount); 16 } 17 } 18 //定义对象实现Runnable,设置线程任务 19 class Input implements Runnable 20 { 21 private Bank b; 22 //使用构造函数保证存钱和显示同一资源 23 public Input(Bank b) 24 { 25 this.b=b; 26 } 27 public void run() 28 { 29 int x=0; 30 while(true) 31 { 32 if(x==0) 33 { 34 b.put("张三",100); 35 } 36 else 37 { 38 b.put("lisi",200); 39 } 40 //0与1之间切换可使用求2的余数 41 x=(x+1)%2; 42 } 43 } 44 } 45 46 //定义对象实现Runnable,设置线程任务 47 class Output implements Runnable 48 { 49 private Bank b; 50 //使用构造函数保证存钱和显示同一资源 51 public Output(Bank b) 52 { 53 this.b=b; 54 } 55 public void run() 56 { 57 while(true) 58 { 59 b.take(); 60 } 61 } 62 } 63 64 class ThreadDemo 65 { 66 public static void main (String[] args) 67 { 68 //同一资源 69 Bank b=new Bank(); 70 71 Input in=new Input(b); 72 Output out=new Output(b); 73 74 // 75 Thread t1=new Thread(in); 76 Thread t2=new Thread(out); 77 78 t1.start(); 79 t2.start(); 80 } 81 }
结果:
出现线程安全,为什么呢?
分析:
解决方法:添加同步(synchronized),同步要添加到什么位置?
添加同步锁的原则:有共享数据被操作的代码上,且同步锁对象要相同(同步方法的同步锁为,此类的对象(this))
代码:
1 class Bank 2 { 3 String name;//存钱人名 4 int amount; 5 6 //同步方法的同步锁为,此类的对象(this) 7 synchronized void put(String name,int amount) 8 { 9 this.name=name; 10 this.amount=amount; 11 } 12 13 synchronized void take() 14 { 15 System.out.println(this.name+"---"+this.amount); 16 } 17 }
结果出现的效果与预想结果不同,预想结果是存一笔,显示一笔:
结果分析:
接下来,想法是:在t1中执行完成后就让t2执行
具体做法:
1、使用一个共享数据(flag)记录t1是否已执行
2、已执行就让t1等待(原因:防止t1再次获取CPU执行权),让t2执行
3、t2执行完成,t2等待,把t1设置成未执行状态(让t1获取CPU执行资格)
1 class Bank 2 { 3 private String name; 4 private int amount; 5 private boolean flag=false; 6 7 synchronized void put(String name,int amount) 8 { 9 //1、判断线程是否已执行,防止t1线程继续获取CUP执行权 10 if(flag) 11 try{this.wait();}catch(InterruptedException e){}//wait()方法,将线程存放在阻塞线程池中,释放CUP执行权和释放同步锁 12 13 this.name=name; 14 this.amount=amount; 15 //已执行赋值 16 this.flag = true; 17 //唤醒阻塞线程池中的第一个线程,获取CUP执行资格 18 this.notify(); 19 } 20 21 synchronized void take() 22 { 23 if(!flag) 24 try{this.wait();}catch(InterruptedException e){} 25 System.out.println(this.name+"---"+this.amount); 26 27 this.flag = false; 28 this.notify(); 29 } 30 }
结果: