多线程设计模式-生产者/消费者模式

定义:通过通道对数据(或任务)的生产者和消费者进行解耦,使二者不直接交互,从而使二者的处理速率相对来说互不影响。

生产者消费模式UML图

Producer: 生产者,负责生产相应的“产品”并将其存入通道
    service: 生产者对外暴露的服务方法
Product: 生产者所“生产”的数据或者任务
Channel: 对通道的抽象。通道充当生产者和消费者之间的缓冲区用于“产品”传递,它可以是有存储容量限制的。
    put: 将“产品”存入通道
    take: 从通道中取出一个“产品”
BlockingQuequChannel: 基于阻塞队列的Channel实现。
    put: 将“产品”存入通道。当队列满时,该方法会将当前线程挂起直到队列非满
    take: 从通道中取出一个“产品”。当队列为空时,该方法通常会将当前线程挂起直到队列非空
Consumer: 消费者,负责对“产品”进行处理
    dispatch: 从通道中获取“产品”,并对其进行处理

生产者/消费者模式非常常见,不仅仅应用在多线程编程中。下列代码中BlockingQueueChannel缓存了一些来不及消费的产品,线程AbstractTerminatableThread充当了消费者,来为文件建立索引,这样产品的生产者和消费者在缓冲队列的帮助下,一定程度上实现了解耦,可以看作是非常简单版的消息中间件。

package com.bruce.producerConsumer;

/**
* @Author: Bruce
* @Date: 2019/6/4 16:10
* @Version 1.0
*/
public interface Channel<P> {

    P take() throws InterruptedException;

    void put(P product) throws InterruptedException;
}
package com.bruce.producerConsumer;

import java.util.concurrent.BlockingQueue;

/**
* @Author: Bruce
* @Date: 2019/6/4 16:16
* @Version 1.0
*/
public class BlockingQueueChannel<P> implements Channel<P> {

    private final BlockingQueue<P> queue;

    public BlockingQueueChannel(BlockingQueue<P> queue) {
        this.queue = queue;
    }

    @Override
    public P take() throws InterruptedException {
        return queue.take();
    }

    @Override
    public void put(P product) throws InterruptedException {
        queue.put(product);
    }
}
package com.bruce.producerConsumer;

import com.bruce.twoPhaseTermination.AbstractTerminatableThread;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.text.Normalizer;
import java.util.concurrent.ArrayBlockingQueue;

/**
* @Author: Bruce
* @Date: 2019/6/4 16:00
* @Version 1.0
*/
public class AttachmentProcessor {
    public static final String ATTACHMENT_STORE_BASE_DIR = "C:/";

    private final Channel<File> channel = new BlockingQueueChannel<File>(
            new ArrayBlockingQueue<File>(200)
    );

    private final AbstractTerminatableThread indexingThread =
    new AbstractTerminatableThread() {
        @Override
        protected void doRun() throws Exception {
            File file = null;

            file = channel.take();
            try {
                indexFile(file);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                terminationToken.reservations.decrementAndGet();
            }
        }

        private void indexFile(File file) throws Exception {
            Thread.sleep(1000);
        }
    };

    public void init() {
        indexingThread.start();
    }

    public void shutdown() {
        indexingThread.terminate();
    }

    public void saveAttachment(InputStream in, String documentId, String originalFileName) throws IOException {
        File file = saveAsFile(in, documentId, originalFileName);
        try {
            channel.put(file);
            indexingThread.terminationToken.reservations.incrementAndGet();
        } catch (InterruptedException e) {
            ;
        }
    }

    private File saveAsFile(InputStream in, String documentId, String originalFileName) throws IOException {
        String dirName = ATTACHMENT_STORE_BASE_DIR + documentId;
        File dir = new File(dirName);
        dir.mkdirs();
        File file = new File(dirName + '/' + Normalizer.normalize(originalFileName, Normalizer.Form.NFC));

        if (!new File(dirName).equals(new File(file.getCanonicalFile().getParent()))) {
            throw new SecurityException("Invalid originalFileName:" + originalFileName);
        }
        try (InputStream dataIn = in) {
            Files.copy(dataIn, Paths.get(file.getCanonicalPath()), StandardCopyOption.REPLACE_EXISTING);
        }
        return file;
    }
}

并发情况下,会有大量的产品同时产生,也会有大量的产品同时消耗,生产者/消费者模式所构建的通道在一定程度上有削峰平谷的作用,有界阻塞队列的使用可以平衡生产者和消费者的处理能力,如果通道满了,那么生产者会被阻塞到消费者消费了一些产品,如果通道空了,那么消费者也会一直等待,直到有产品出现为止。除了使用有界阻塞队列构建通道,具有流量控制效果的无界阻塞队列也是可以达到相同的效果,例如LinkedBlockingQueue.

参考资料

黄文海 Java多线程编程实战指南(设计模式篇)

黄文海的Github

猜你喜欢

转载自blog.csdn.net/u010145219/article/details/91348440