线程间的通信
概念:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。 比如:线程A用来生成包子的,线程B用来吃包子的,包子可以理解为同一资源,线程A与线程B处理的动作,一个 是生产,一个是消费,那么线程A与线程B之间就存在线程通信问题。
多个线程在处理同一个资源,并且任务不同时,需要线程通信来帮助解决线程之间对同一个变量的使用或操作。 就 是多个线程在操作同一份数据时, 避免对同一共享变量的争夺。也就是我们需要通过一定的手段使各个线程能有效 的利用资源。而这种手段即—— 等待唤醒机制。
等待唤醒机制:
涉及方法:
1、wait(): 让线程处于冻结状态,被wait的线程会存储到线程池中
2、notify():唤醒线程池中任一线程(随机)
3、nitifyAll():唤醒线程池中所有线程
这些方法必须定义在同步中,因为这些方法是用于操作线程状态的方法,必须要明确操作的是哪个锁上的线程
这些方法都定义在Object类中,因为这些方法都是监视器的方法,监视器其实就是锁,锁可以是任意的对象,任意的对象调用的方式一定定义在Object类中
哪怕只通知了一个等待的线程,被通知线程也不能立即恢复执行,因为它当初中断的地方是在同步块内,而 此刻它已经不持有锁,所以她需要再次尝试去获取锁(很可能面临其它线程的竞争),成功后才能在当初调 用 wait 方法之后的地方恢复执行
如果能获取锁,线程就从 WAITING 状态变成 RUNNABLE 状态;
否则,从 wait set 出来,又进入 entry set,线程就从 WAITING 状态又变成 BLOCKED 状态
生产者与消费者问题
包子铺线程生产包子,吃货线程消费包子。当包子没有时(包子状态为false),吃货线程等待,包子铺线程生产包子 (即包子状态为true),并通知吃货线程(解除吃货的等待状态),因为已经有包子了,那么包子铺线程进入等待状态。 接下来,吃货线程能否进一步执行则取决于锁的获取情况。如果吃货获取到锁,那么就执行吃包子动作,包子吃完(包 子状态为false),并通知包子铺线程(解除包子铺的等待状态),吃货线程进入等待。包子铺线程能否进一步执行则取 决于锁的获取情况。
代码演示:
在代码中一定要注意是否有在等待的线程没有被唤醒
包子对象
public class baozi {
String name;
boolean flag;
}
包子铺线程:
public class baozidian extends Thread{
// 定义一个包子对象
baozi baoz;//方便下面的使用
public baozidian(String name,baozi bz){//构造方法
// 使用Thread父类的构造方法 参数为name
super(name);
// 将传递进来的包子对象赋给上面定义好的包子对象
this.baoz = bz;
}
@Override
public void run() {
// 获取线程的名字
String name = Thread.currentThread().getName();
// 循环十次
for(int i = 0; i<10;i++){
//线程同步 (当括号中的包子对象是一样的时候,只有一个线程可以运行)一旦获取资源必须将同步代码块中的代码执行完毕之后才开始进行下一次的抢夺
synchronized (baoz){
// 如果包子的数量为true时 即有包子的时候
if(baoz.flag){
try {
// 当循环不是最后一次的时候让线程等待
if(i!=9){
baoz.wait();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}else{
System.out.println("包子吃完了"+ name+"开始制作"+baoz.name);
// 更改包子数量的状态
baoz.flag = true;
//唤醒同一资源下的其他线程
baoz.notify();
}
}
}
}
}
吃货线程:
public class chihuo extends Thread{
baozi baoz;
// 构造方法
public chihuo (String name,baozi bz){
// 使用Thread父类的构造方法 参数为name
super(name);
this.baoz = bz;
}
@Override
public void run() {
// 获取当前线程的名字
String name = Thread.currentThread().getName();
for(int i = 0;i<10;i++){
synchronized (baoz){
// 当包子存在时
if(baoz.flag){
// 吃包子
System.out.println(name +"正在吃"+baoz.name);
// 改变包子的状态
baoz.flag = false;
// 唤醒同一资源下的其他线程
baoz.notify();
}else{
try {
if(i!=9){
// 等待
baoz.wait();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
测试类:
public class Main {
public static void main(String[] args) {
baozi bz = new baozi();
bz.name = "猪肉大葱";
bz.flag = true;
chihuo ch = new chihuo("孙康宝",bz);
baozidian bzd = new baozidian("早餐店",bz);
ch.start();
bzd.start();
}
}
运行结果如下: