1.分析
这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件。
- 对于生产者,没有生产产品之前,要通知消费者等待,而生产了产品之后,有需要马上通知消费者消费
- 对于消费者,消费完之后,要通知生产者已经结束消费,需要生产新的产品以供消费
- 生产者消费者问题中,仅有synchronized是不够的
- synchronized可阻止并发更新同一个共享资源,实现了同步
- synchronized不能用来实现不同线程之纪检的消息传递
2.线程通信提供的方法
方法名 | 作用 |
wait() | 表示一直等待,知道其他线程进行通知,与sleep(抱着锁睡着)不同,会释放锁(不占用资源)。 |
wait(long timeout) | 指定等待的毫秒数。 |
notify() | 唤醒一个等待状态的线程(随机唤醒) |
notifyAll() | 唤醒一个对象中上所有被调用wait()方法的线程,优先级高的,优先被调度。 |
注意:均是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常IIIegalMonitorStateException
3.解决方法1
并发协作模型“生产者/消费者模式”--->管程法
- 生产者:负责生产数据的模块(可能是方法,对象,线程,进程);
- 消费者:负责处理数据的模块(可能是方法,对象,线程,进程);
- 缓冲区:消费者不能直接使用生产者的数据,他们之间有个缓冲区
生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据
package com.thread.senior;
/**
* @description: 测试生产者消费者----->管程法
* @author:wt
* @createTime:2021/11/15 15:27
* @version:1.0
*/
public class TestPC {
public static void main(String[] args) {
SynContainer synContainer = new SynContainer();
Producer producer = new Producer(synContainer);
Consumer consumer = new Consumer(synContainer);
producer.start();
consumer.start();
}
}
class Producer extends Thread{
//需要容器
SynContainer container;
public Producer(SynContainer container){
this.container = container;
}
// 进行生产鸡
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println("生产了第"+ i + "只鸡!");
container.Product(new Chicken(i));
}
}
}
class Consumer extends Thread{
//需要容器
SynContainer container;
public Consumer(SynContainer container){
this.container = container;
}
// 进行消费鸡
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println("消费者吃了第"+ container.consume().id + "只鸡" );
}
}
}
class Chicken {
int id ; // 鸡的编号
public Chicken(int id){
this.id = id ;
}
}
class SynContainer {
// 缓冲区的容器
Chicken[] chickens = new Chicken[10];
// 容器计数器
int count = 0;
//生产者放入产品
public synchronized void Product(Chicken chicken){
// 如果容器满了,生产者需要等待消费者消费
if(count == chickens.length) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
chickens[count] = chicken;
count ++;
// 缓冲区里面有鸡了,开始通知消费者消费了
this.notifyAll();
}
public synchronized Chicken consume(){
// 如果容器是空的,消费者不能进行消费,需要等到生产者生产鸡
if(count == 0 ) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//开始吃鸡了,取得是最后一只,并进行保存!
count -- ;
Chicken eatChicken = chickens[count];
// 我已经把鸡吃了,开始通知生产者继续生产了
this.notifyAll();
return eatChicken;
}
}
4.解决方法2
并发协作模型“生产者/消费者模式”--->信号灯法
给出一个标志,如果为真给一个true,如果为假给一个false。
package com.thread.senior;
/**
* @description: 测试生产者消费者----->信号灯法
* @author:wt
* @createTime:2021/11/15 18:25
* @version:1.0
*/
public class TestSignal {
public static void main(String[] args) {
Cctv cctv = new Cctv();
new Palyer(cctv).start();
new Viewer(cctv).start();
}
}
class Palyer extends Thread{
// 需要cctv平台表演
Cctv cctv;
public Palyer( Cctv cctv) {
this.cctv=cctv;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i%2==0){
cctv.perform("表演今平没");
}else {
cctv.perform("拍抖音短视频");
}
}
}
}
class Viewer extends Thread{
// 需要cctv平台表演
Cctv cctv;
public Viewer( Cctv cctv) {
this.cctv=cctv;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("观众正在观看"+ cctv.view());
}
}
}
// 相当于一个信号塔,通过标识来通知生产者消费者
class Cctv {
// 演员节目
String item ;
// 信号标识
boolean flag = true;
//表演节目
public synchronized void perform (String item ){
//是否需要表演
if (!flag){
// 请等待观众看完上场表演
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.flag= !this.flag;
// 开始表演节目
this.item=item;
System.out.println("演员"+ item);
//通知
this.notifyAll();
}
//观看节目
public synchronized String view (){
//是否有观看的表演
if (flag){
//正在表演,请等待观看
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.flag= !this.flag;
this.notifyAll();
return this.item;
}
}
5.应用场景:生产者和消费者问题
- 假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费。
- 如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产者等待,直到仓库的产品被消费者取走为止。
- 如果仓库中没有产品,则消费者将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止。