执行者框架 - 生产者消费者模式

时间:2017-10-24 00:45:27

标签: java multithreading design-patterns io executorservice

Java_author在第5.3.1节中提到了

  

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

     

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

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

如果是,那么生产者 - 消费者模式的概念如何有助于Executor框架的实施?

2 个答案:

答案 0 :(得分:1)

Executor framework使用producer-consumer模式。

来自维基百科,

  

在计算中,生产者 - 消费者问题(也称为   有界缓冲问题)是多进程的典型例子   同步问题。问题描述了两个过程,即   生产者和消费者共享一个共同的,固定大小的缓冲区   作为队列。制作人的工作是生成数据,将其放入   缓冲,然后重新开始。与此同时,消费者正在消费   数据(即,从缓冲区中删除),一次一个。该   问题是确保生产者不会尝试添加数据   缓冲区,如果它已满并且消费者不会尝试删除数据   来自空缓冲区。

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

  1. 提交和保存作业的队列
  2. 使用提交给队列的任务的线程数。
  3. 根据执行程序服务的类型,这些参数会发生变化

    例如,

    • 固定线程池使用LinkedBlockingQueue并且用户配置了no of threads
    • 缓存线程池根据提交的任务数使用SynchronousQueue0之间没有线程

答案 1 :(得分: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. 制作人Runnable API中添加Callableexecute workQueue.offer(command)

  2. execute()方法会根据需要创建Worker个帖子

  3. Worker线程以无限循环运行。它从Runnable

  4. 获取任务(例如getTask()) 在getTask()
  5. BlockingQueue<Runnable> workQueue)个游泳池,然后选择Runnable。它是BlockingQueue消费者

  6.   

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

    是的,如上所述。

      

    如果是,生产者 - 消费者模式的概念如何帮助实施Executor框架?

    BlockingQueueArrayBlockingQueue实现ExecutorService这样的

    ThreadPoolExecutor实现是线程安全的。程序员明确实现同步,等待和通知调用以实现相同的开销已经减少了。