【问题标题】:Java Producer Consumer - Why not a simple FixedThreadPool?Java Producer Consumer - 为什么不是简单的 FixedThreadPool?
【发布时间】:2019-06-19 13:53:50
【问题描述】:

我正在阅读一些关于 Java 中的生产者消费者实现的内容。 大多数在线示例代码显示了具有以下基本对象的解决方案:

用作数据管道的阻塞队列

n 个 Producer 将消息推送到队列中

n 个正在从队列中轮询数据的消费者

通常情况下,消费者会被提交到一个完成实现的线程池。

我调查的另一个选项,几乎没有在线表示,只是将任务作为可运行对象提交到 FixedThreadPool。
在这个实现中,处理消息的业务逻辑在 Runnable 对象的run() 方法中实现。当我们有一条新消息要处理时,我们可以简单地将该类型的新任务提交给FixedThreadPool,然后......就是这样。运行线程的数量由FixedThreadPool 实现与轮询消息的逻辑一起管理,剩下的就是我们用例的业务逻辑。
谁能解释为什么这个解决方案被忽视了?
当 Java 语言已经为我们实现了阻塞队列和轮询时,我们是否需要使用阻塞队列和轮询?

public class ProducerConsumerExample{

    private ExecutorService pool;

    public ProducerConsumerExample(int numberOfThreads){
        this.pool = Executors.newFixedThreadPool(numberOfThreads);
    }

    public void submit(MessageObject msg){
        pool.submit(new MessagePrinter(msg));
    }

}

public class MessagePrinter implements  Runnable{
    private MessageObject msg;

    public MessagePrinter(MessageObject msg){
        this.msg = msg;
    }

    @Override
    public void run() {
        //only need to implement logic that is releavent for our use case
        System.out.println("Message recieved " + msg.toString());
    }
}

public static void main(String[] args){
    ProducerConsumerExample ex = new ProducerConsumerExample(5);
    for(int i=0;i<WHATEVER;i++){
        ex.submit(new MessageObject());
    }
}

【问题讨论】:

  • Re,“......为什么这个解决方案被忽视了?”是什么让你认为它被忽视了?您的问题表明您正在阅读有关生产者/消费者模式的信息。将任务分配给线程池是一种不同的模式。可能有很多问题可以通过任何一种方式解决,但是如果您专门寻找有关生产者/消费者的信息,那么您不应该如果您没有找到有关如何最有效地使用线程池的大量信息,您会感到惊讶。
  • P.S.;使用线程池来启动长时间运行的“消费者”线程有点像将方形钉安装到圆孔中。在这种情况下,您真正​​要做的是使用线程池代替线程 factory。 Java 标准库定义了一个ThreadFactory 接口,以及获取系统默认ThreadFactory 的方法。看看吧!

标签: java multithreading runnable executorservice


【解决方案1】:

你的方法没有问题,只是不清楚Executors.newFixedThreadPool().创建的池中使用了什么队列,如果任务数量变得相当大,你确定你不能得到RejectedExecutionException吗?

因此最好使用具有预定义大小的任务队列的新显式创建。 看Executors.newFixedThreadPool(int)的源码。

【讨论】:

  • Re,“......不明显......由 Executors.newFixedThreadPool() 使用什么队列”。将其视为的代理...在这里创建和配置满足应用程序需求的线程池...。 Java 标准库提供了大量用于创建和配置 ThreadPoolExecutor 实例的选项,包括:允许应用程序注入它自己的BlockingQueue 实例、它自己的ThreadFactory 实例等。newFixedThreadPool() 是一种快速且简单的方法,可以让线程池在小型/一次性应用程序中运行良好,并且stackoverflow sn-ps。
【解决方案2】:

实际上,在生产者-消费者应用程序中最好使用线程池。来自 Java Concurrency in Practice:

Executor 基于生产者-消费者模式,其中活动 提交任务的是生产者(生产单位 完成)和执行任务的线程是消费者(消费 那些工作单元)。使用 Executor 通常是最简单的方法 在您的应用程序中实现生产者-消费者设计。

选择实现时应考虑的事项:

  • 耦合:生产者和消费者对彼此以及将它们连接在一起的渠道了解多少?
  • 底层队列实现:哪种类型的Queue 最适合这项工作?
  • 生命周期:如果ThreadPool 损坏或关闭会怎样?
  • 线程管理和饥饿:您在管理线程方面参与了多少?长寿命对象与短寿命对象?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-04
    • 2019-09-19
    • 2017-05-02
    • 2017-03-18
    相关资源
    最近更新 更多