Producer-Consumer模型:二、如何实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/billll/article/details/52900737

Producer-Consumer模型的实现主要考虑一下几个方便:

  • 生成者的实现
  • 消费者的实现
  • 消息队列
  • 线程的通信、等待、同步
  • 线程的终止

1.Producer

生产者负责把请求加入队列,如果队列已满则等待或者返回错误。

public class Producer{
    // 消息队列
    private LinkedList<Request> buffer = null;
    // 队列的容量
    private int maxBufferSize = 5;
    private NormalLoggerI logger;

    public Producer(LinkedList<Request> buffer, int maxBufferSize, NormalLoggerI logger) {
        this.buffer = buffer;
        this.maxBufferSize = maxBufferSize;
        this.logger = logger;
    }

    public boolean add(Request request) {
        try {
            // 请求队列是竞争资源,只有一道进程可以读写
            synchronized (buffer) {
                 while (buffer.size() == maxBufferSize) {
                     try {
                         logger.debug("buffer is full, Producer thread waiting for consumer to take something from buffer");
                         // 当请求队列为满的状态时,producer需要等待consumer从队列中取走请求
                         buffer.wait(); 
                     }
                     catch (Exception ex) { 
                         logger.exception(ex);
                     } 
                 }

                 buffer.add(request);
                 // 添加新的请求后通知所有的consumer
                 buffer.notifyAll();
            }
            return true;
        }
        catch (Exception ex) {
            logger.error("Producer : fail to add request");
            logger.exception(ex);
            return false;
        }
    }
}

这里需要注意的是buffer作为多个线程共享的对象必须进行同步保护。否则buffer中请求的数量可能超过maxBufferSize。原因很简单,如果我们设定maxBufferSize = 5,此时buffer为空,此时此刻有六个producer通过访问buffer,那么他们读取buffer.size()的时候值都为0,于是6个producer同时执行add方法,最终buffer中请求的数量超过了5。

2. Consumer

消费者负责中队列中取走请求,如果队列为空消费者进入等待状态,直到新的请求到达。

public class Consumer extends Thread {
    // 消息队列
    private LinkedList<Request> buffer = null;
    private NormalLoggerI logger;

    public Consumer(LinkedList<Request> buffer, NormalLoggerI logger) {
        this.buffer = buffer;
        this.logger = logger;
    }

    @Override 
    public void run() {
        while (true) {
            try {
                Request request = null;
                synchronized (buffer) {
                    while (buffer.isEmpty()) {
                        logger.debug("Queue is empty, Consumer thread is waiting for producer thread to put something in queue");
                        try {
                            // 当消息队列为空时,消费者等待
                            buffer.wait();
                            // 如果client线程通过调用消费者线程的中断方法,并且测试队列为空,则消费者线程退出
                            if (buffer.size() == 0 && Thread.currentThread().isInterrupted()) {
                                logger.debug("Consumer thread is exit in the waiting");
                                return;
                            }
                        }
                        catch (InterruptedException ex) {
                            // 如果消费者线程在等待的过程中接收到来自主线程的中断请求,则消费者线程退出
                            logger.debug("Consumer thread is exit beacouse of InterruptedException");
                            return;
                        }
                        catch (Exception ex) {
                            logger.exception(ex);
                        }
                    }
                    // 从队列中获取一个请求
                    request = buffer.pop();
                    // 通知生产者进程:可以继续添加消息
                    buffer.notifyAll();
                }

                // Do Something

                // 在处理完消息后如果消费者线程检测到中断状态,并且此时队列为空,则消费者线程立即退出
                synchronized (buffer) {
                    if (buffer.size() == 0 && Thread.currentThread().isInterrupted()) {
                        logger.debug("Consumer thread is exit beacouse of Interrupte status");
                        return;
                    }
                }
            }
            catch (Exception ex) {
                logger.exception(ex);
            }
        }

    }
}

3. Service

Service的作用是管理生产者、消费者、队列、以及服务的启动和停止。

需要注意的是生产者在添加消息之前必须检查服务是否被停止,如果停止则返回错误。

public class Service{
    private static Producer producer = null;
    private static List<Consumer> consumers = new ArrayList<Consumer>();
    private static LinkedList<Request> buffer = new LinkedList<Request>();
    // isServiceStopped对于Service来说是全局共享的所以标注为static
    private static boolean isServiceStopped;

    public static void init() {
        producer = new Producer(buffer);
        consumers.add(new Consumer(buffer)) ;
        consumers.add(new Consumer(buffer)) ;
        isServiceStopped = false;
        // 启动所有的consumer
        for (Consumer c : consumers) {
            c.start();
        }
    }

    public static boolean addRequest(String type, String text, String mobile) {
        try {
            // addReuest方法可能在多个client进程中被访问,所以必须使用同步机制,但是这会导致性能问题
            synchronized(isServiceStopped) {
                if (isServiceStopped) {
                    return false;
                }
                // 添加消息
                Request request = new Request();
                return producer.add(request);
            }
        }
        catch (Exception ex) {
            logger.exception(ex);
            return false;
        }
    }

    public static void stop() {
        synchronized(isServiceStopped) {
            // 发送中断请求
            for (Consumer c : consumers) {
                c.interrupt();
            }
            isServiceStopped = true;
        }
    }
}

4. 不足

4.1 Consumer的异常处理

在这个Producer-Consumer模型的实现版本中如果consumer在处理请求的过程中发生异常,我只做了记录日志的处理。如果能实现retry就更好了。

4.2 Service的新能瓶颈

你是否注意到在Service的视线中addRequest()和stop()方法都需要锁住isServiceStopped,而isServiceStopped是一个静态变量。也就是说在同一时刻所有的client线程中只有一道线程可以访问addRequest或者stop,这将导致client线程的等待,特别是在大并发的时候。
解决方法也很简单,可以使用volatile特性,这是一个轻量级的锁。

猜你喜欢

转载自blog.csdn.net/billll/article/details/52900737
今日推荐