等待唤醒机制
1 线程间通信
概念:多个线程在处理同⼀个资源,但是处理的动作(线程的任务)却不相同
⽐如:线程A⽤来⽣成包⼦的,线程B⽤来吃包⼦的,包⼦可以理解为同⼀资源,线程A与线程B
处理的动作,⼀个是⽣产,⼀个是消费,那么线程A与线程B之间就存在线程通信问题。
为什么要处理线程间的通信:
多个线程并发执⾏时, 在默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成⼀
件任务,并且我们希望他们有规律的执⾏,那么多线程之间需要⼀些协调通信,以此来帮我们达
到多线程共同操作⼀份数据。
如何保证线程通信有效资源利用率:
多个线程在处理同⼀个资源,并且任务不同时,需要线程通信来帮助解决线程之间对同⼀个变量
的使⽤或操作。就是多个线程在操作同⼀份数据时,避免对同⼀共享变量的争夺。也就是我们需
要通过⼀定的⼿段使各个线程能有效 的利⽤资源。⽽这种⼿段即 – 等待唤醒机制。
2 等待唤醒机制
等待唤醒机制
这是多个线程间的⼀种协作机制。
在一个线程进行规定操作后,就进入了等待状态(wait()),等待其他线程执行完他们的指定代码过后
后再将其唤醒( notify() );在有多个线程进⾏等待时,如果需要,可以使⽤ notifyAll() 来唤醒所有的等待线程
wait/notify 就是线程之间的一种协作机制。
等待唤醒中的⽅法
等待唤醒机制就是⽤于解决线程间通信的问题的,使⽤到的3个⽅法的含义如下:
- **wait:**线程不再活动,不再参与调度,进⼊ wait set 中,因此不会浪费 CPU 资源,也不会
去竞争锁了,这时的线程状态即是 WAITING。它还要等着别的线程执⾏⼀个特别的动作,
也即是“通知( notify )”在这个对象上等待的线程从 wait set 中释放出来,重新进⼊到调度队
列( ready queue )中。 - **notify:**则选取所通知对象的 wait set 中的⼀个线程释放;例如,餐馆有空位置后,等候就
餐最久的顾客最先⼊座。 - **notifyAll:**则释放所通知对象的 wait set 上的全部线程
调⽤wait和notify⽅法需要注意的细节
1.wait 方法与notify 方法 必须要由同一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用wait方法后的进程。
2. wait⽅法与notify⽅法是属于Object类的⽅法的。因为:锁对象可以是任意对象,⽽任意对
象的所属类都是继承了Object类的。
3. wait⽅法与notify⽅法必须要在同步代码块或者是同步函数中使⽤。因为:必须要通过锁对
象调⽤这2个⽅法。
3 案例 双端通信:模拟图片加载与显示的过程
需求:
- 线程load:
1.加载图片,模拟加载过程
2.加载完成,设置图片状态 isLoad =true;
3.唤醒show线程
4.判断图片是否显示完成,如果没有完成,则等待
5.被唤醒,继续下载图片,模拟下载完成 - 线程show:
1.判断图片是否加载完成,若没有完成,则等待
2.被唤醒,显示图片,模拟显示过程
3.显示完成,设置图片状态 isShow=true;
4.唤醒load线程
实现:
1.定义共享资源类 Picture
// 线程的共享对象 Picture
public class Picture {
boolean isLoad; //判断是否加载完成
boolean isShow; // 判断是否显示完成
}
2.LoadPicture 线程
public class LoadPicture extends Thread {
// 定义共享资源picture
private Picture picture;
public LoadPicture(Picture picture) {
this.picture = picture;
}
// 加载图片,模拟加载过程
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println("正在加载..." + i + "%");
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 2.加载完成,设置图片状态 isLoad =true;
System.out.println("加载完成");
picture.isLoad = true;
//唤醒ShowLoad线程
synchronized (picture) {
picture.notify();
}
//判断图片是否显示完成,如果没有完成,则等待
if (!picture.isShow) {
System.out.println("等待图片显示");
synchronized (picture) {
try {
picture.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//被唤醒,继续下载图片,模拟下载完成
for (int i = 1; i <= 100; i++) {
System.out.println("正在下载..." + i + "%");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("下载完成");
}
}
3.ShowPicture线程
public class ShowPicture extends Thread {
private Picture picture;
public ShowPicture(Picture picture) {
this.picture = picture;
}
@Override
public void run() {
//如果isLoad是false 则线程等待
if (!picture.isLoad) {
System.out.println("等待加载完成");
synchronized (picture) {
try {
picture.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(2000);
System.out.println("图片显示完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//显示完成,设置图片状态 isShow=true;
picture.isShow = true;
//唤醒load线程 notify()
synchronized (picture) {
picture.notify();
}
}
}
4.测试类
public class TestDemo {
public static void main(String[] args) {
Picture picture = new Picture();
// 两个线程共享同一个资源对象 picture
Thread load = new LoadPicture(picture);
Thread show = new ShowPicture(picture);
load.start();
show.start();
}
}
4 生产者与消费者问题
生产者线程⽣产商品,消费者线程消费商品。
1.当商品没有时(商品池子为空 isEmpty 为true),消费者线程等待
2.生产者线程⽣产商品(商品池子不为空 isEmpty 为false),并通知消费者线程( 解除消费者的等待状态 )
3.有商品时,那么生产者线程进⼊等待状态。接下来,消费者线程能否进⼀步执⾏则取决于 锁的获取情况。
如果消费者获取到锁,那么就执⾏消费动作,商品买完( 商品池子为空 isEmpty 为true ),
并通知生产者线程( 解除生产者的等待状态 ),消费者线程进⼊等待。
生产者线程能否进⼀步执⾏则取决于锁的获取情况
定义 共享对象 Product
public class Product {
//初始商品数 5
int count = 5;
// 此时商品池子为isEmpty=false
boolean isEmpty=false;
}
定义消费者线程
public class Consumer extends Thread {
Product product;
public Consumer(Product product) {
this.product = product;
}
@Override
public void run() {
while (true) {
// 商品池子为空 则消费者等待 并通知生产者
if (product.isEmpty == true) {
System.out.println("消费者:厂家没货了");
synchronized (product) {
try {
product.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//有货
System.out.println("消费");
product.count--;
if (product.count == 0)
//商品个数为0 则商品池子isEmpty= true;
product.isEmpty= true;
synchronized (product) {
// 唤醒生产者线程
product.notify();
}
}
}
}
}
定义生产者线程:
public class Production extends Thread {
Product product = new Product();
public Production(Product product) {
this.product = product;
}
@Override
public void run() {
while (true) {
if (product.isEmpty == false) {
synchronized (product) {
try {
product.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 没有产品 生产产品
product.count += 2;
if (product.count > 0) {
System.out.println("生产者:产品已生产,快来购买");
product.isEmpty = false;
synchronized (product) {
//唤醒消费者线程
product.notify();
}
}
}
}
}
测试类:
public class TestDemo {
public static void main(String[] args) {
Product product = new Product();
Production production = new Production(product);
Consumer consumer = new Consumer(product);
production.start();
consumer.start();
}
}