生产者-消费者问题详解(代码片段)

author author     2022-12-30     212

关键词:

1. 前言

  生产者-消费者问题是经典的线程同步问题(我会用java和c分别实现),主要牵扯到三个点:
 一:能否互斥访问共享资源(不能同时访问共享数据);
 二:当公共容器满时,生产者能否继续生产(生产者应阻塞并唤醒消费者消费);
 三:当公共容器为空时,消费者能否继续消费(消费者应阻塞并唤醒生产者生产)。

2. JAVA实现

step0:在java中我们创建线程是通过继承Thread类或者继承Runnable接口并实现他的run方法来实现的,这里我们采用后者
step1:定义一个放馒头的大筐(一个公共的容器类),这个筐具有push方法和pop方法,分别对应往筐中放馒头和从筐中取出馒头。由于在同一个时间段内只能有一个线程访问此方法,so,我们给这两个方法加锁。代码如下:

class SyncStack//定义放馒头的筐,是栈,先进后出
    int index = 0;//定义筐里面馒头的编号
    WoTou[] arrWT = new WoTou[6];//定义一个引用类型的数组

    public synchronized void push(WoTou wt)//定义往筐里放馒头的方法,由于需要保证在一段特定时间里只能有一个线程访问此方法,所以用synchronized关键字
        while(index == arrWT.length)
            try
                this.wait();
            catch(InterruptedException e)
                e.printStackTrace();
            
        
        this.notify();
        arrWT[index] = wt;
        index ++;
    

    public synchronized WoTou pop()//定义从筐里往外拿馒头的方法,同理在一段时间只能有一个线程访问此方法,所以用synchronized关键字
        while(index == 0)
            try
                this.wait();//当前的正在我这个对象访问的这个线程wait
            catch(InterruptedException e)
                e.printStackTrace();
            
        
        this.notify();//唤醒一个等待的线程,叫醒一个正在wait在我这个对象上的线程
        index --;
        return arrWT[index];
    

step2:分别定义生产者和消费者的类,他们均是不同的线程。给出代码:

class Producer implements Runnable//定义生产者这个类,是一个线程
    SyncStack ss = null;//声明了筐子的引用变量ss,表示做馒头的人往那个筐里放馒头
    Producer(SyncStack ss)
        this.ss = ss;
    

    public void run()
        for(int i=0; i<20; i++)
            WoTou wt = new WoTou(i);//new出一个馒头,该馒头的编号为i
            ss.push(wt);//把第i个馒头放到筐中
            System.out.println(i);
            System.out.println("生产了:" + wt);    
            try
                Thread.sleep((int)(Math.random() * 200));//每生产一个睡眠1s
            catch (InterruptedException e)
                e.printStackTrace();
            
        
    


class Consumer implements Runnable//定义消费者这个类,也是一个线程
    SyncStack ss = null;//声明了筐子的引用变量ss,表示吃馒头的人往那个筐里拿馒头
    Consumer(SyncStack ss)
        this.ss = ss;
    

    public void run()
        for(int i=0; i<20; i++)
            WoTou wt = ss.pop();//取出一个馒头
            System.out.println("消费了:" + wt);//开始吃馒头

            try
                Thread.sleep((int)(Math.random() * 1000));
                //每消费一个睡眠1s
            catch (InterruptedException e)
                e.printStackTrace();
            
        
    

step3:我们事先定义了一个馒头类,现在给出测试类测试:

/*
wait和sleep的区别
    -1:sleep不需要唤醒,线程不会释放对象锁,属于Thread类
    -2:wait需要notify()方法唤醒,线程会放弃对象锁,属于Object类
*/
public class ProducerConsumer
    public static void main(String[] args)
        SyncStack ss = new SyncStack();
        Producer p = new Producer(ss);
        Consumer c = new Consumer(ss);

        new Thread(p).start();
        new Thread(c).start();
    


class WoTou//定义馒头这个类
    int id;//定义馒头的编号
    WoTou(int id)
        this.id = id;
    
    public String toString()//重写toString方法
        return "WoTou:" + id;
    

step4:看下测试结果,发现符合我们事先说的那三点:
技术分享图片

3. C实现

step0:c语言在Windows下实现线程的需要导入#include<process.h>头文件,用_beginthread();来开始一个线程,用_endthread();来结束一个线程。具体操作方法,自行百度。
step1:C语言中缓冲区对应公共容器,我们通过定义互斥信号量mutex来实现线程对缓冲池的互斥访问。直接看下代码操作:

#include<stdio.h>
#include<process.h>
#define N 10

//代表执行生产和消费的变量 
int in=0, out=0;

//线程结束的标志 
int flg_pro=0, flg_con=0;

//mutex:互斥信号量,实现线程对缓冲池的互斥访问;
//empty和full:资源信号量,分别表示缓冲池中空缓冲池和满缓冲池的数量(注意初值,从生产者的角度) 
int mutex=1, empty=N, full=0;

//打印测试 
void print(char c)
    printf("%c    一共生产了%d个窝头,消费了%d个窝头,现在有%d个窝头
", c, in, out, full);


//请求某个资源 
void wait(int *x)
    while((*x)<=0);
    (*x)--;


//释放某个资源 
void signal(int *x)
    (*x)++;
 

//生产者 
void produce(void *a)
    while(1)
//      printf("开始阻塞生产者
"); 
        wait(&empty);   //申请一个缓冲区,看有无其他线程访问 
        wait(&mutex);
//      printf("结束阻塞生产者
");

        in++;

        signal(&mutex);  
        signal(&full);  //full加一,唤醒消费者,告诉消费者可以消费 

//      printf("生产者执行。。。
");
        print(‘p‘);

        Sleep(200);
        if(flg_pro == 1)
            _endthread();
           
     
 

//消费者
void consumer(void * a)
    while(1)
//      printf("开始阻塞消费者
");
        wait(&full);
        wait(&mutex);
//      printf("结束阻塞消费者
");

        out++;

        signal(&mutex);
        signal(&empty);
//      printf("消费者执行。。。
");
        print(‘c‘);

        Sleep(200);
        if(flg_con == 1)
            _endthread();   
        
    
 

//主函数 
int main()
    _beginthread(consumer,0,NULL);  
    _beginthread(produce,0,NULL);
    //总的执行时间为1分钟 
    Sleep(10000);
    flg_pro=flg_con=1;
    system("pause");
    return 0;

step2:注意事项:
  1)用来实现互斥的wait(&mutex);signal(&mutex);必须成对出现在每一个线程中,对于资源信号量的waitsignal操作,分别成对出现在不同的线程中
  2)先执行对资源信号量的wait操作,在执行对互斥信号量的wait操作,不能颠倒否则导致死锁。
step3:测试结果,符合预期:
技术分享图片

4. 总结

 现在缺乏的是一种把生活中具体的问题抽象成代码的能力,可能也是对c语言的不熟悉导致的问题,看着我宿舍大神写的代码,真漂亮,由衷的羡慕。熟知并非真知,还得多加思考才是。

高并发编程学习——线程通信详解(代码片段)

...m/2019/11/26/gao-bing-fa-bian-cheng-xue-xi-1-bing-fa-ji-chu/一、经典的生产者消费者案例上一篇文章我们提到一个应用可以创建多个线程去执行不同的任务,如果这些任务之间有着某种关系,那么线程之间必须能够通信来协调完成工作。生产... 查看详情

深入了解androidhandler机制原理详解(代码片段)

...ff1a;子线程handler主线程其实构成了线程模型中的经典问题生产者-消费者模型。生产者-消费者模型:生产者和消费者在同一时间段内共用同一个存储空间,生产者往存储空间中 查看详情

java生产消费模型—arrayblockingqueue详解(代码片段)

...上,服务员负责取走(消费)美食。这里,厨师就扮演着生产者的身份,美食是生产的内容,服务员就扮演 查看详情

spring集成rabbitmq配置文件详解(生产者和消费者)(代码片段)

1,首先引入配置文件org.springframework.amqp,如下:<dependency><groupId>org.springframework.amqp</groupId><artifactId>spring-rabbit</artifactId><version>1.7.1.RELEASE</versio 查看详情

kafka2.5.0生产者与消费者配置详解(代码片段)

1)引入maven依赖我这里使用的是springboot2.1.3.RELEASE 版本:<dependency><groupId>org.springframework.kafka</groupId><artifactId>spring-kafka</artifactId></dependency>会引入一对的ka 查看详情

生产者消费者模型详解(代码片段)

生产者消费者模型文章目录生产者消费者模型什么是生产者消费者模型基于BlockingQueue的生产者消费者模型单生产者单消费者模型多生产者多消费者模型什么是生产者消费者模型生产者消费者模式就是通过一个容器来解决生产者... 查看详情

生产者消费者模型详解(代码片段)

生产者消费者模型文章目录生产者消费者模型什么是生产者消费者模型基于BlockingQueue的生产者消费者模型单生产者单消费者模型多生产者多消费者模型什么是生产者消费者模型生产者消费者模式就是通过一个容器来解决生产者... 查看详情

rocketmq—消费者客户端详解(代码片段)

...ceComuserpublicstaticvoidmain(String[]args)throwsException//实例化消息生产者,指定组名DefaultMQP 查看详情

kafkaconsumer架构设计剖析和源码全流程详解(代码片段)

...系统,最重要的两个功能便是,往Kafka生产数据的生产者KafkaProducer,和从Kafka拉取数据消费的消费者KafkaConsumer。今天我们主要讲解消费者,KafkaConsumer。我习惯从最朴素的问题开启对知识的探索:1、我们的消费... 查看详情

上下架(代码片段)

...中间件对比⑵.介绍2.环境搭建3.入门案例⑴.模块搭建⑵.生产者发送消息⑶.消费者接收消息⑷.测试⑸.消费者组①.一对一②.一对多4.分区机制5.高可用设计⑴.集群⑵.备份机制(Replication)6.生产者详解⑴.发送类型⑵.参数详解①... 查看详情

rocketmq详解(代码片段)

...xff0c;RocketMQ的特点是纯JAVA实现1.基础概念Producer:消息生产者,负责产生消息,一般由业务系统负责产生消息ProducerGroup:消息生产者组,简单来说就是多个发送同一类消息的生产者称之为一个生产者Consumer:... 查看详情

activemq高级用法详解(代码片段)

...个流程然后对ActiveMQ的特性有个大的理解ActiveMQ特性详解生产者产生消息,发送到MQ中,而MQ递送给消费者,MQ中有接收,存储,发送的几个概念。最后交给消费者。对于mq无论是接收 查看详情

万字详解linux系列多线程(下)(代码片段)

...(2)代码使用(3)关于pthread_cond_wait二、生产者消费者模型1.什么是生产者消费者模型2.相关概念(1)一个交易场所(2)三种角色(3)三种关系3.基于阻塞队列的单生产者、单消费者模型&#x... 查看详情

javajava实现的生产者和消费者问题(代码片段)

查看详情

多线程应用——生产者消费者问题(代码片段)

前言生产者和消费者问题是多线程模型中的经典问题:生产者和消费者在同一时间段内共用同一个存储空间,生产者往存储空间中添加产品,消费者从存储空间中取走产品,当存储空间为空时,消费者阻塞,当存储空间满时,生... 查看详情

生产者和消费者相关问题(代码片段)

将生产者和消费者问题深入理解、融会贯通。1.书上课后练习P187-43semaphoreoffer=1,P1,P2,P3,sugar,water,orange;intmutex=1;beginprocess_offer()  while(offer==0)    P(mutex);    P(offer);    offer();    if(offer(orange))      P(ora 查看详情

生产者和消费者问题(代码片段)

1//生产者和消费者问题2//使用环形队列3//解决了生产过剩的问题4#include<stdio.h>5#include<semaphore.h>6#include<pthread.h>7#include<stdlib.h>8#include<time.h>9#include<unistd.h>1011//定义环形队列类型1 查看详情

秒杀多线程第十篇生产者消费者问题(代码片段)

   继经典线程同步问题之后,我们来看看生产者消费者问题及读者写者问题。生产者消费者问题是一个著名的线程同步问题,该问题描述如下:有一个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了... 查看详情