了解Java FixedThreadPool

时间:2015-03-26 11:56:29

标签: java multithreading executorservice callable

我试图理解Java FixedThreadPool在实践中是如何工作的,但是文档没有回答我的问题。

假设一个简单的场景,如:

ExecutorService ES= Executors.newFixedThreadPool(3);
List<Future> FL;
for(int i=1;i<=200;i++){
   FL.add(ES.submit(new Task()));
}
ES.shutdown();

其中TaskCallable,它构造一些资源,使用它们并返回一些输出。

我的问题:完成Task循环后内存中有多少for?换句话说:一次只有3 Task构建资源,或者所有资源都是预先创建的,这样,.submit我有200 Task(及其资源)等待执行?

注意:资源构建发生在Task的构造函数中,而不是call()方法中。

在javadoc (随意跳过以下内容):让我感到困惑的是Java文档中的以下说明

  

创建一个重用固定数量的线程的线程池   关闭共享的无界队列。在任何时候,最多nThreads线程   将是积极的处理任务。

我想这意味着,在我的例子中,所有200个任务都在队列中,但其中只有3个随时执行。

非常感谢任何帮助。

5 个答案:

答案 0 :(得分:10)

您的代码等同于

for (int i = 1; i <= 200; i++){
    Task t = new Task();
    FL.add(ES.submit(t));
}

在for循环之后,Task的构造函数因此被调用了200次,因此它包含的代码被执行了200次。是否将任务提交给执行者是无关紧要的:您在循环中调用构造函数200次,并且每个任务构建完成后,将其提交给遗嘱执行人。执行者不是调用任务构造函数的人。

答案 1 :(得分:6)

任务将从队列中逐个删除,因此在执行过程中,任务将被删除,只有它们的结果将存储在那些Future对象中。

所以基本上在记忆中:

3个螺纹
200 - &gt; 0任务
0 - &gt; 200未来
(每个执行的任务)

答案 2 :(得分:4)

您正在使用new Task()创建200个对象,并将这些任务提交给执行者。执行程序包含对此Task对象的引用。 因此,如果在Task的构造函数中构建并保存资源,则所有200任务都将保留资源。

如果可能,如果您不希望200个实例构建和保留资源,则可以在Task的调用方法中构建和使用该资源。在这种情况下,一次只有3 Task将构建并保留资源。

答案 3 :(得分:3)

创建所有200个任务并使用资源,所有这些资源都在队列中。

当一个空闲线程可用于执行时,线程池只会逐个调用它们的run()/ call()方法。

答案 4 :(得分:3)

要理解这一点,您需要查看在循环中将任务提交给Executor时发生的情况。 首先,我们将看一下向Executor提交单个任务。我现在将引用JDK 1.7.0_51源代码

静态方法Executor.newFixedThreadPool方法返回一个ThreadPoolExecutor,其中包含一个阻塞队列来保存任务

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

当您向此Executor添加任务时,这将转到ThreadPoolExecutor的提交方法AbstractExecutorService,其中写入了提交方法的实现。

public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }

execute方法是特定于实现的(意味着不同类型的Executor以不同的方式实现)

现在真正的肉。这是ThreadPoolExecutor中定义的执行方法。 特别注意评论。 这里很少有像corePoolSize这样的ThreadPoolExecutor的配置参数。

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        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);
    }