等待唤醒机制
消费者线程输出数据,那么如果本身没有数据了,需要等待生产者线程产生数据,通知生产者线程生产数据.
2.模式图解
注:在多线程环境下完成此模式各线程应针对同一对象进行才操作
等待唤醒机制
1.目的:等待唤醒机制用于解决线程间通信的问题
2.方法
(1)public final voidwait():告诉当前线程放弃执行权,并放弃监视器(锁)并进入阻塞状态,直到其他线程持有获得执行权,并持有了相同的监视器(锁)并调用notify为止。
注:线程等待,阻塞式方法,调用就会立即释放锁
(2)public final voidnotify():唤醒持有同一个监视器(锁)中调用wait的第一个线程,例如,餐馆有空位置后,等候就餐最久的顾客最先入座。
注:被唤醒的线程是进入了可运行状态。等待cpu执行权。
(3)public final void notifyAll():唤醒持有同一监视器中调用wait的所有的线程。
3.注:调用wait和notify方法需要注意的细节
(1)wait方法与notify方法必须要由同一个锁对象调用(对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程)
(2)wait方法与notify方法是属于Object类的方法的(锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的)
(3)wait方法与notify方法必须要在同步代码块或者是同步函数中使用(必须要通过锁对象调用这两个方法)
4.waite和sleep方法的区别:
(1)相同点:
线程调用wait()或者是sleep()方法都会进入临时阻塞状态,会释放cpu的执行权。并且调用时都需要处理 InterruptedException异常(中断异常)
(2)不同点:
wait(): 释放资源,释放锁。是Object的方法。
sleep():释放资源,不释放锁。是Thread的静态方法
问题:wait(),notify(),notifyAll 方法为什么都定义到Object类而不是Thread类中?
答:使用同步方法解决线程安全问题,wait()和notify() 方法可以释放锁,而Object类可以代表任意对象
例:
// 测试类
public class Test {
public static void main(String[] args) {
// 创建产品类对象
Product product = new Product();
// 创建资源对象
Set set = new Set(product);
Get get = new Get(product);
// 创建线程类对象
Thread t1 = new Thread(set);
Thread t2 = new Thread(get);
// 启动线程
t1.start();
t2.start();
}
}
// 产品类
class Product {
String name;
String number;
boolean flag;
}
// 生产者线程
class Set implements Runnable {
private Product product;
public Set(Product product) {
this.product = product;
}
private int i = 0;
@Override
public void run() {
while (true) {
synchronized (product) {
// 进行判断有没有数据
if (product.flag) {
try {
product.wait(); // 处于等待状态,并立即释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 产生数据
if (i % 2 == 0) {
product.name = "产品1";
product.number = "001";
} else {
product.name = "产品2";
product.number = "002";
}
i++;
// 此时有数据了,修改标记
product.flag = true;
// 唤醒消费者线程
product.notify();
}
}
}
}
// 消费者线程
class Get implements Runnable {
private Product product;
public Get(Product product) {
this.product = product;
}
@Override
public void run() {
while (true) {
synchronized (product) {
// 判断有没有数据
if (!product.flag) {
try {
product.wait(); // 线程等待,并立即释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 消费数据
System.out.println(product.name + "---" + product.number);
// 消费完毕,修改标记
product.flag = false;
// 唤醒生产者线程
product.notify();
}
}
}
}
例2:将产品类中的成员变量私有化,使用同步方法
// 测试类
public class Test {
public static void main(String[] args) {
Product product = new Product();
Set set = new Set(product);
Get get = new Get(product);
Thread t1 = new Thread(set);
Thread t2 = new Thread(get);
t1.start();
t2.start();
}
}
// 产品类
public class Product {
private String name;
private String number;
private boolean flag;
private int i;
public synchronized void set() {
// 判断有没有数据
if (this.flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 生产数据
if (i % 2 == 0) {
this.name = "产品1";
this.number = "001";
} else {
this.name = "产品2";
this.number = "002";
}
i++;
// 修改标记
this.flag = true;
// 唤醒
this.notify();
}
public synchronized void get() {
// 判断有没有数据
if (!this.flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 消费数据
System.out.println(this.name + ":" + this.number);
// 修改标记
this.flag = false;
// 唤醒
this.notify();
}
}
// 生产者线程
public class Set implements Runnable {
private Product product;
public Set(Product product) {
this.product = product;
}
@Override
public void run() {
while(true) {
product.set();
}
}
}
// 消费者线程
public class Get implements Runnable {
private Product product;
public Get(Product product) {
this.product = product;
}
@Override
public void run() {
while(true) {
product.get();
}
}
}