概念:多个线程在处理同一个资源 但是处理的动作 (线程的任务)却不相同
比如:线程A用来生产包装的 线程B用来吃包子的 包子可以理解为同一资源 线程A于线程B处理的动作 一个是生产一个是消费 那么线程A与线程B之间就存在线程通信问题 多个线程并发执行时 在默认情况下CPU是随机切换线程的 当我们需要多个线程来共同完成一件任务 并且我们希望他们由规律的执行 那么多线程之间需要一些协调通讯 以此来帮我们达到多线程共同操作一份数据
等待方法
1.调用wait方法的前提是获得这个对象的锁(synchronized对象锁,如果有多个线程取竞争这个锁,只有一个线程获得锁,其他线程会处于等待队列)
2.使当前执行代码的线程进行等待 . ( 把线程放到等待队列中 )
3.调用wait方法会释放锁
4.满足一定条件会重新尝试获得这个锁,被唤醒的之后不是立即恢复执行,而是进入阻塞队列,竞争锁
唤醒方法
notify
notify()随机唤醒一个处在等待状态的线程
notifyAll()唤醒所有处在等待状态的线程
方法notify()也要在同步方法或同步块中调用,该方法是用来通知那些可能等待该对象的对象锁的 其它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。
如果有多个线程等待,则有线程调度器随机挑选出一个呈 wait 状态的线程。(并没有 "先来后到") 在notify()方法后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出同步代码块之后才会释放对象锁。
结束等待的三个方式
1.其他线程调用该对象的 notify 方法.
2.wait 等待时间超时 (wait 方法提供一个带有 timeout 参数的版本, 来指定等待时间).
3.其他线程调用该等待线程的 interrupted 方法, 导致 wait 抛出 InterruptedException 异常
生产者与消费者问题:
等待唤醒机制其实就是经典的 “生产者与消费者”的问题。
举例:生产包子和消费包子
包子铺线程生产包子,吃货线程消费包子。当包子没有时,(包子状态为false),吃货线程等待,包子铺线程生产包子(包子状态为true),并通知吃货线程(解除吃货的等待状态)。因为已经有包子了,那么包子铺线程进入等待状态。接下来,吃货线程能否进一步执行则取决于锁的获取情况。如果吃货获取到锁,那么就执行吃包子的动作,包子吃完(包子状态为false),并通知包子铺线程(接触包子铺的等待状态),吃货线程进入等待。包子铺线程能否进一步执行则取决于锁的获取状态。
案例
//包子铺
package KeTangTest.XianChengTongXun;
/*
资源类对象:多个线程共同操作的对象
*/
public class BaoZi {
String name; //包子名称
boolean flag; //包子的状态,(true表示存在,false表示不存在)
}
//吃货
package KeTangTest.XianChengTongXun;
public class ChiHuo extends Thread{
BaoZi baoZi;
//构造方法:用来指定线程的名字和要操作的资源
public ChiHuo(String name,BaoZi bz){
super(name);
this.baoZi=bz;
}
/**
* 对于吃货-----消费者:
* 如果包子 不存在 进入等待状态
* 如果包子 存在 进入执行状态,吃包子。吃完之后包子变为不存在 此时唤醒早餐店制作包子
*/
@Override
public void run() {
String threadName=Thread.currentThread().getName();
int count=0;
while (true){
synchronized (baoZi){
count++;
if (count>10){
break;
}
if (baoZi.flag){ //如果包子存在
System.out.println(threadName+"开始吃"+baoZi.name); //吃包子
baoZi.flag=false; //修改状态
baoZi.notify(); //唤醒同一资源下的其他线程
}else {
try {
baoZi.wait(); //进入等待状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
//早餐店
package KeTangTest.XianChengTongXun;
public class ZaoCanDian extends Thread{
BaoZi baoZi;
//构造方法:用来指定线程的名字和要操作的资源
public ZaoCanDian(String name,BaoZi bz){
super(name);
this.baoZi=bz;
}
/**
* 对于早餐店------生产者
* 如果包子 不存在 进入执行状态,制造包子。 制作完毕包子存在,唤醒吃货
* 如果包子 存在 进入等待状态
*/
@Override
public void run() {
String threadName=Thread.currentThread().getName();
int count=0;
while (true) {
synchronized (baoZi) {
count++;
if (count > 10) {
break;
}
if (baoZi.flag) { //如果包子存在
try {
baoZi.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else { //如果包子不存在
System.out.println(threadName+"开始制作"+baoZi.name); //制作包子
baoZi.flag=true; //更改包子状态
baoZi.notify(); //唤醒同一资源下的其他线程
}
}
}
}
}
运行-测试
package KeTangTest.XianChengTongXun;
public class ThreadTest {
public static void main(String[] args) {
//定义资源对象
BaoZi baoZi=new BaoZi();
baoZi.name="猪肉香菇";
baoZi.flag=true;
//定义两个线程,起名字且操作同一个对象
ChiHuo ch=new ChiHuo("八戒",baoZi);
ZaoCanDian zcd=new ZaoCanDian("好运来早餐",baoZi);
//自动线程
zcd.start();
ch.start();
}
}