针对多个不同线程之间操作同一个数据的情况,被称为线程之间的通信.
而这个操作不但要考虑到线程的同步问题还有一个隐藏的问题
如果这些操作之间是有一定的顺序的,比如 A-->B-->C
那么就不应该是谁获得了执行权就执行谁,这时候java为我们提供了等待唤醒机制
问题描叙
比如某个车行轮流生产宝马与奔驰两种汽车,然后消费者购买生产的汽车
简单案例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| public static void main(String[] args) { Car s = new Car(); Product product = new Product(s); Consumer consumer = new Consumer(s); Thread product1 = new Thread(product,"车行1"); Thread consumer1 = new Thread(consumer,"消费者1"); product1.start(); consumer1.start(); } class Car { private String name; private int money; public synchronized void set(String name,int money){ this.name = name; this.money = money; } public synchronized Object[] get(){ Object[] carInformation = {new StringBuilder().append(name).toString(),money}; return carInformation; } } class Product implements Runnable { private Car s; public Product() {} public Product(Car s) {this.s = s;} int x=0; @Override public void run() { while (true) { if (x % 2 == 0) { s.set("宝马",30); System.out.println(Thread.currentThread().getName()+" 已生产 : 宝马,售价(万) : 30"); } else { s.set("奔驰",31); System.out.println(Thread.currentThread().getName()+" 已生产 : 奔驰,售价(万) : 31"); } x++; try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Consumer implements Runnable { private Car s; public Consumer(){} public Consumer(Car s) {this.s = s;} @Override public void run() { while (true) { Object[] carInformation = s.get(); System.out.println(Thread.currentThread().getName()+" 购买:" +carInformation[0] + "---" + carInformation[1]); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
|
其执行结果如下 :

问题分析
其实上述代码存在一个问题,那就是消费者不一定会在指定的时间来消费产品
正常的情况应该是,先把汽车生产出来,等被消费完了就再生产一个
而不是上面这种指定时间的情况
假如车行最快10ms就能生产一辆车,而处于旺季,那么就应该以最快的速度生产
并且消费者即使想要购买,也要等车行生产出来才行
而在淡季假如消费者1000ms才买一辆车,那么就不应该是100ms生产一辆
等待唤醒机制
根据分析,可以知道.多个线程的操作之间需要一个传呼的工具
也就是,消费者需要知道有东西可以消费了,执行;否则等待
生产者知道东西被消费了,执行;否则等待
这时候使用共享数据是最合适的,只需要在共享数据内定义标记信息就可以完成
由于数据的类型根据不同的场景必定不同,故而java在Object类中定义了相关方法
与之相关的方法
在调用等待方法的时候,会释放掉当前的锁对象,故而等待唤醒代码可以放到同步代码块里面
1 2 3 4 5 6 7 8 9 10
| public final void wait() throws InterruptedException
public final void wait(long timeout) throws InterruptedException
public final void wait(long timeout, int nanos) throws InterruptedException
public final void notify()
public final void notifyAll()
|
对上述代码的改进 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class Car { .. private boolean flag = false; public synchronized void set(String name,int money){ if (flag){ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } this.name = name; this.money = money; flag = true; notify(); } public synchronized Object[] get(){ if (!flag){ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } Object[] carInformation = {new StringBuilder().append(name).toString(),money}; flag = false; notify(); return carInformation; } }
|
执行结果如下 :
