【问题标题】:Executor framework - Producer Consumer pattern执行者框架——生产者消费者模式
【发布时间】:2017-10-24 00:45:26
【问题描述】:

Java_author在5.3.1小节中提到,

...许多生产者-消费者设计可以使用Executor 任务执行框架来表达,它本身使用生产者-消费者模式。

...生产者-消费者模式提供了一种线程友好的方法,可以将问题分解为更简单的组件(如果可能的话)。


Executor 框架实现内部是否遵循生产者-消费者模式?

如果是,生产者-消费者模式的思想如何帮助Executor框架的实现?

【问题讨论】:

  • 你可以看看ThreadPoolExecutor类。
  • @BongCon 目前在第 1 部分,最后一章。我还没有开始讨论ThreadPoolExecutor 的第 2 部分。很难直接跳到第 2 部分。
  • 这确实是生产者消费者模式不是吗?..主类将产生要执行的任务,而执行者将通过执行来消耗该任务..

标签: java multithreading design-patterns io executorservice


【解决方案1】:

检查ThreadPoolExecutor的实现

public void execute(Runnable command) {
    int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    else if (!addWorker(command, false))
        reject(command);
}

现在检查

private boolean addWorker(Runnable firstTask, boolean core) {
     // After some checks, it creates Worker and start the thread
    Worker w = new Worker(firstTask);
    Thread t = w.thread;

   // After some checks, thread has been started
   t.start();
}

Worker的实现:

  /**
     * Class Worker mainly maintains interrupt control state for
     * threads running tasks, along with other minor bookkeeping.
     * This class opportunistically extends AbstractQueuedSynchronizer
     * to simplify acquiring and releasing a lock surrounding each
     * task execution.  This protects against interrupts that are
     * intended to wake up a worker thread waiting for a task from
     * instead interrupting a task being run.  We implement a simple
     * non-reentrant mutual exclusion lock rather than use ReentrantLock
     * because we do not want worker tasks to be able to reacquire the
     * lock when they invoke pool control methods like setCorePoolSize.
     */
    private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {

      /** Delegates main run loop to outer runWorker  */
       public void run() {
            runWorker(this);
       }

    final void runWorker(Worker w) {
          Runnable task = w.firstTask;
          w.firstTask = null;
          boolean completedAbruptly = true;
    try {
        while (task != null || (task = getTask()) != null) {
            w.lock();
            clearInterruptsForTaskRun();
            try {
                beforeExecute(w.thread, task);
                Throwable thrown = null;
                try {
                    task.run();
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }

执行哪个Runnable取决于以下逻辑。

/**
 * Performs blocking or timed wait for a task, depending on
 * current configuration settings, or returns null if this worker
 * must exit because of any of:
 * 1. There are more than maximumPoolSize workers (due to
 *    a call to setMaximumPoolSize).
 * 2. The pool is stopped.
 * 3. The pool is shutdown and the queue is empty.
 * 4. This worker timed out waiting for a task, and timed-out
 *    workers are subject to termination (that is,
 *    {@code allowCoreThreadTimeOut || workerCount > corePoolSize})
 *    both before and after the timed wait.
 *
 * @return task, or null if the worker must exit, in which case
 *         workerCount is decremented
 */
private Runnable getTask() {
     // After some checks, below code returns Runnable

      try {
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
}

总结:

  1. Producerexecute API 中添加RunnableCallableworkQueue.offer(command)

  2. execute() 方法在需要时创建Worker 线程

  3. 这个Worker 线程在无限循环中运行。它从getTask()获取任务(例如Runnable

  4. getTask() 池在 BlockingQueue&lt;Runnable&gt; workQueue) 上并占用 Runnable。是BlockingQueue消费者

Executor 框架实现内部是否遵循生产者-消费者模式?

是的,如上所述。

如果是,生产者-消费者模式的思想对 Executor 框架的实现有何帮助?

BlockingQueueArrayBlockingQueueExecutorService implementationThreadPoolExecutor 这样的实现是线程安全的。显式实现同步、等待和通知调用的程序员开销已减少。

【讨论】:

  • 所以,Worker 线程最初被创建(corePoolSize),因为初始command 传递给execute()。在我的命令在此Worker 线程上完成执行后,相同的Worker 线程从队列中选择新任务。不是吗?
  • 是的。 corePoolSize 稍后会增加。
  • 因此,在此code 中,运行java ThreadPoolTest 5 2,创建大小为5 的workQueue 和大小为2 的workers 作为ThreadPoolExecutor 实例的成员。对吗?
【解决方案2】:

Executor framework 使用 producer-consumer 模式。

来自维基百科,

在计算中,生产者-消费者问题(也称为 有界缓冲区问题)是多进程的经典示例 同步问题。问题描述了两个过程, 生产者和消费者,他们共享一个共同的、固定大小的缓冲区 作为队列。生产者的工作是生成数据,放入 缓冲,然后重新开始。同时,消费者在消费 数据(即从缓冲区中删除),一次一个。这 问题是确保生产者不会尝试将数据添加到 缓冲区已满且消费者不会尝试删除数据 从一个空的缓冲区。

如果我们看看不同的ExecutorService framework 实现,更具体地说是ThreadPoolExecutor 类,它基本上有以下内容:

  1. 一个队列,作业在其中提交和保存
  2. 消耗提交到队列的任务的线程数。

根据执行器服务的类型,这些参数会有所变化

例如,

  • 固定线程池使用LinkedBlockingQueue 和用户配置的线程数
  • 缓存线程池使用SynchronousQueue0Integer.MAX_VALUE 之间的线程数,具体取决于提交的任务数

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多