[Java并发]-04-ThreadPoolExecutor类创建线程池对象和源码分析

来源:互联网 发布:java servlet 文件接口 编辑:程序博客网 时间:2024/06/12 19:27

Executors底层会创建ThreadPoolExecutor或

1 核心构造器

    public ThreadPoolExecutor(int corePoolSize,                              int maximumPoolSize,                              long keepAliveTime,                              TimeUnit unit,                              BlockingQueue<Runnable> workQueue,                              ThreadFactory threadFactory,                              RejectedExecutionHandler handler) {


  •         corePoolSize 个线程保持在线程池中,即使他们是空闲的,除非设置allowCoreThreadTimeOut
  •         maximumPoolSize是池中允许的最大线程数。
  •         keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
  •         unit - keepAliveTime 参数的时间单位。
  •         workQueue - 执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务。
  •         threadFactory - 执行程序创建新线程时使用的工厂。
  •         handler - 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。


核心和最大池大小
当新任务在方法 execute(java.lang.Runnable) 中提交时,如果运行的线程少于 corePoolSize,则创建新线程来处理请求,即使其他辅助线程是空闲的。
如果运行的线程多于 corePoolSize 而少于 maximumPoolSize,则仅当队列满时才创建新线程。

如果设置的 corePoolSize 和 maximumPoolSize 相同,则创建了固定大小的线程池。

如果将 maximumPoolSize 设置为基本的无界值(如 Integer.MAX_VALUE),则允许池适应任意数量的并发任务。

在大多数情况下,核心和最大池大小仅基于构造来设置,不过也可以使用 setCorePoolSize(int)setMaximumPoolSize(int) 进行动态更改。 



创建新线程

使用 ThreadFactory 创建新线程。如果没有另外说明,则在同一个ThreadGroup 中一律使用Executors.defaultThreadFactory() 创建线程,并且这些线程具有相同的NORM_PRIORITY 优先级和非守护进程状态。通过提供不同的 ThreadFactory,可以改变线程的名称、线程组、优先级、守护进程状态,等等。如果从newThread 返回 null 时ThreadFactory 未能创建线程,则执行程序将继续运行,但不能执行任何任务。


2. 使用SynchronousQueue的线程池

public static class Task implements Callable<String> {private String id;Task(String id) {this.id = "Task-" + id;}@Overridepublic String call() throws Exception {try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(id + "   " + Thread.currentThread().getName());return id;}}public static void main(String[] args) {System.out.println("=========main start");final ThreadPoolExecutor exec = new ThreadPoolExecutor(1, 128,                        10000L, TimeUnit.MILLISECONDS,                        new SynchronousQueue<Runnable>(),                        new ThreadFactory() {        @Override        public Thread newThread(Runnable r) {        return new Thread(r);        }        });ConcurrencyUtils.printThreadPoolExecutorInfo(exec);for (int i = 0; i < 10; i++) {exec.submit(new Task(String.valueOf(i)));}do {ConcurrencyUtils.printThreadPoolExecutorInfo(exec);try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}}while(true);}

=========main start
===================Sat Nov 15 18:24:59 CST 2014========================
exec.getPoolSize()=0
exec.getCorePoolSize()=1
exec.getLargestPoolSize()=0
exec.getMaximumPoolSize()=128
exec.getActiveCount()=0
exec.getTaskCount()=0
exec.getCompletedTaskCount()=0
===================Sat Nov 15 18:24:59 CST 2014========================
exec.getPoolSize()=10
exec.getCorePoolSize()=1
exec.getLargestPoolSize()=10
exec.getMaximumPoolSize()=128
exec.getActiveCount()=10
exec.getTaskCount()=10
exec.getCompletedTaskCount()=0
Task-3   Thread-3
Task-1   Thread-1
Task-0   Thread-0
Task-2   Thread-2
Task-6   Thread-6
Task-8   Thread-8
Task-7   Thread-7
Task-4   Thread-4
Task-9   Thread-9
Task-5   Thread-5
===================Sat Nov 15 18:25:04 CST 2014========================
exec.getPoolSize()=10
exec.getCorePoolSize()=1
exec.getLargestPoolSize()=10
exec.getMaximumPoolSize()=128
exec.getActiveCount()=0
exec.getTaskCount()=10
exec.getCompletedTaskCount()=10
===================Sat Nov 15 18:25:09 CST 2014========================
exec.getPoolSize()=10
exec.getCorePoolSize()=1
exec.getLargestPoolSize()=10
exec.getMaximumPoolSize()=128
exec.getActiveCount()=0
exec.getTaskCount()=10
exec.getCompletedTaskCount()=10
===================Sat Nov 15 18:25:14 CST 2014========================
exec.getPoolSize()=1
exec.getCorePoolSize()=1
exec.getLargestPoolSize()=10
exec.getMaximumPoolSize()=128
exec.getActiveCount()=0
exec.getTaskCount()=10
exec.getCompletedTaskCount()=10
===================Sat Nov 15 18:25:19 CST 2014========================
exec.getPoolSize()=1
exec.getCorePoolSize()=1
exec.getLargestPoolSize()=10
exec.getMaximumPoolSize()=128
exec.getActiveCount()=0
exec.getTaskCount()=10
exec.getCompletedTaskCount()=10



3. 使用ArrayBlockingQueue的线程池


public static class Task implements Callable<String> {private String id;Task(String id) {this.id = "Task-" + id;}@Overridepublic String call() throws Exception {try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(id + "   " + Thread.currentThread().getName());return id;}}public static void main(String[] args) {System.out.println("=========main start");final ThreadPoolExecutor exec = new ThreadPoolExecutor(1, 128,                        10000L, TimeUnit.MILLISECONDS,                        new ArrayBlockingQueue<Runnable>(3),                        new ThreadFactory() {        @Override        public Thread newThread(Runnable r) {        return new Thread(r);        }        });ConcurrencyUtils.printThreadPoolExecutorInfo(exec);for (int i = 0; i < 10; i++) {exec.submit(new Task(String.valueOf(i)));}do {ConcurrencyUtils.printThreadPoolExecutorInfo(exec);try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}}while(true);}

=========main start
===================Sat Nov 15 18:29:56 CST 2014========================
exec.getPoolSize()=0
exec.getCorePoolSize()=1
exec.getLargestPoolSize()=0
exec.getMaximumPoolSize()=128
exec.getActiveCount()=0
exec.getTaskCount()=0
exec.getCompletedTaskCount()=0
===================Sat Nov 15 18:29:56 CST 2014========================
exec.getPoolSize()=7
exec.getCorePoolSize()=1
exec.getLargestPoolSize()=7
exec.getMaximumPoolSize()=128
exec.getActiveCount()=7
exec.getTaskCount()=10
exec.getCompletedTaskCount()=0
Task-4   Thread-1
Task-7   Thread-4
Task-8   Thread-5
Task-5   Thread-2
Task-0   Thread-0
Task-9   Thread-6
Task-6   Thread-3
Task-2   Thread-4
Task-3   Thread-5
Task-1   Thread-1
===================Sat Nov 15 18:30:01 CST 2014========================
exec.getPoolSize()=7
exec.getCorePoolSize()=1
exec.getLargestPoolSize()=7
exec.getMaximumPoolSize()=128
exec.getActiveCount()=0
exec.getTaskCount()=10
exec.getCompletedTaskCount()=10
===================Sat Nov 15 18:30:06 CST 2014========================
exec.getPoolSize()=7
exec.getCorePoolSize()=1
exec.getLargestPoolSize()=7
exec.getMaximumPoolSize()=128
exec.getActiveCount()=0
exec.getTaskCount()=10
exec.getCompletedTaskCount()=10
===================Sat Nov 15 18:30:11 CST 2014========================
exec.getPoolSize()=1
exec.getCorePoolSize()=1
exec.getLargestPoolSize()=7
exec.getMaximumPoolSize()=128
exec.getActiveCount()=0
exec.getTaskCount()=10
exec.getCompletedTaskCount()=10
===================Sat Nov 15 18:30:16 CST 2014========================
exec.getPoolSize()=1
exec.getCorePoolSize()=1
exec.getLargestPoolSize()=7
exec.getMaximumPoolSize()=128
exec.getActiveCount()=0
exec.getTaskCount()=10
exec.getCompletedTaskCount()=10


4. 使用LinkedBlockingQueue的线程池

public static class Task implements Callable<String> {private String id;Task(String id) {this.id = "Task-" + id;}@Overridepublic String call() throws Exception {try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(id + "   " + Thread.currentThread().getName());return id;}}public static void main(String[] args) {System.out.println("=========main start");final ThreadPoolExecutor exec = new ThreadPoolExecutor(1, 128,                        10000L, TimeUnit.MILLISECONDS,                        new LinkedBlockingQueue<Runnable>(),                        new ThreadFactory() {        @Override        public Thread newThread(Runnable r) {        return new Thread(r);        }        });ConcurrencyUtils.printThreadPoolExecutorInfo(exec);for (int i = 0; i < 10; i++) {exec.submit(new Task(String.valueOf(i)));}do {ConcurrencyUtils.printThreadPoolExecutorInfo(exec);try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}}while(true);}


=========main start
===================Sat Nov 15 18:31:31 CST 2014========================
exec.getPoolSize()=0
exec.getCorePoolSize()=1
exec.getLargestPoolSize()=0
exec.getMaximumPoolSize()=128
exec.getActiveCount()=0
exec.getTaskCount()=0
exec.getCompletedTaskCount()=0
===================Sat Nov 15 18:31:31 CST 2014========================
exec.getPoolSize()=1
exec.getCorePoolSize()=1
exec.getLargestPoolSize()=1
exec.getMaximumPoolSize()=128
exec.getActiveCount()=1
exec.getTaskCount()=10
exec.getCompletedTaskCount()=0
Task-0   Thread-0
Task-1   Thread-0
===================Sat Nov 15 18:31:36 CST 2014========================
exec.getPoolSize()=1
exec.getCorePoolSize()=1
exec.getLargestPoolSize()=1
exec.getMaximumPoolSize()=128
exec.getActiveCount()=1
exec.getTaskCount()=10
exec.getCompletedTaskCount()=2
Task-2   Thread-0
Task-3   Thread-0
Task-4   Thread-0
===================Sat Nov 15 18:31:41 CST 2014========================
exec.getPoolSize()=1
exec.getCorePoolSize()=1
exec.getLargestPoolSize()=1
exec.getMaximumPoolSize()=128
exec.getActiveCount()=1
exec.getTaskCount()=10
exec.getCompletedTaskCount()=5
Task-5   Thread-0
Task-6   Thread-0
===================Sat Nov 15 18:31:46 CST 2014========================
exec.getPoolSize()=1
exec.getCorePoolSize()=1
exec.getLargestPoolSize()=1
exec.getMaximumPoolSize()=128
exec.getActiveCount()=1
exec.getTaskCount()=10
exec.getCompletedTaskCount()=7
Task-7   Thread-0
Task-8   Thread-0
Task-9   Thread-0
===================Sat Nov 15 18:31:51 CST 2014========================
exec.getPoolSize()=1
exec.getCorePoolSize()=1
exec.getLargestPoolSize()=1
exec.getMaximumPoolSize()=128
exec.getActiveCount()=0
exec.getTaskCount()=10
exec.getCompletedTaskCount()=10
===================Sat Nov 15 18:31:56 CST 2014========================
exec.getPoolSize()=1
exec.getCorePoolSize()=1
exec.getLargestPoolSize()=1
exec.getMaximumPoolSize()=128
exec.getActiveCount()=0
exec.getTaskCount()=10
exec.getCompletedTaskCount()=10
===================Sat Nov 15 18:32:01 CST 2014========================
exec.getPoolSize()=1
exec.getCorePoolSize()=1
exec.getLargestPoolSize()=1
exec.getMaximumPoolSize()=128
exec.getActiveCount()=0
exec.getTaskCount()=10
exec.getCompletedTaskCount()=10



5 线程池工作规则(重点)

认真看了上面三个demo的执行结果,不难理解

ThreadPoolExecutor是这样一种存在当任务数小于corePoolSize时候,即便有线程空闲,也会创建新的线程,当任务数量大于等于于corePoolSize的时候就会放到队列里面,

如果无法将请求加入队列,就增加新的线程,如果线程多于corePoolSize并且空闲下来会根据keepAliveTime和unit参数在某时间 来关闭线程。

如果keepAliveTime为0则用完随即关闭(试过100个task,getLargestPoolSize()却是7,然后增加task的执行时间,getLargestPoolSize()会增大)


直到线程数线程超出 maximumPoolSize的时候,在这种情况下,任务将被拒绝,使用最后一个参数handler来处理(RejectedExecutionHandler)。




ThreadPoolExecutor对象是和队列形态相关的,有三种形式的队列,

1. LinkedBlockingQueue是无界队列,如果池中的线程数量少于corePoolSize则创建新的线程即便是有线程空闲。如果待处理的任务数量超过了corePoolSize则放到队列里面,因为队列无界,所以除非线程过多导致崩溃或是内存溢出,task会一直往队列里面放,不再分配新的线程。

也就是说当队列是LinkedBlockingQueue对象的时候 maximumPoolSize参数没啥用,因为线程数量不会大于corePoolSize了,构造时候会判断maximumPoolSize>=corePoolSize否则抛参数异常。

2. ArrayBlockingQueue是有界队列,如上所述,当线程数等于corePoolSize时候就把待处理的任务放入请求队列,当队列满了的时候,会增加新的线程,直到线程maximumPoolSize,如果还有任务无法处理则会回调RejectedExecutionHandler接口


3.SynchronousQueue使用直接提交策略

SynchronousQueue是无界的,也就是说他存数任务的能力是没有限制的,但是由于该Queue本身的特性在某次添加元素后必须等待其他线程取走后才能继续添加。在这里不是核心线程便是新创建的线程

{关于阻塞队列 见《集合框架》}

所以在使用SynchronousQueue通常要求maximumPoolSize是无界的,这样就可以避免上述情况发生(如果希望限制就直接使用有界队列)。

对于使用SynchronousQueue的作用jdk中写的很清楚:此策略可以避免在处理可能具有内部依赖性的请求集时出现锁
什么意思?如果你的任务A1,A2有内部关联,A1需要先运行,那么先提交A1,再提交A2,当使用SynchronousQueue我们可以保证,A1必定先被执行,在A1么有被执行前,A2不可能添加入queue中


getLargestPoolSize()表示池中同时存在线程数 的最大值,不是分配过的线程数。

getPoolSize()线程池中当前线程数。

getCorePoolSize() 就是corePoolSize的属性值(第一个参数)

getMaximumPoolSize()就是第二个参数对相应的属性值。


getActiveCount()活动的任务数
getTaskCount()总任务数
getCompletedTaskCount()完成的任务数。



验证一下SynchronousQueue特性


public static class Task implements Callable<String> {private String id;Task(String id) {this.id = "Task-" + id;}@Overridepublic String call() throws Exception {System.out.println("call start" +id + "   " + Thread.currentThread().getName());try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("call end" +id + "   " + Thread.currentThread().getName());return id;}}public static void main(String[] args) {System.out.println("=========main start");final ThreadPoolExecutor exec = new ThreadPoolExecutor(5, 128,                        0L, TimeUnit.MILLISECONDS,                        new SynchronousQueue<Runnable>(),                        new ThreadFactory() {        @Override        public Thread newThread(Runnable r) {        return new Thread(r);        }        });for (int i = 0; i < 10; i++) {exec.submit(new Task(String.valueOf(i)));}}


=========main start
call startTask-0   Thread-0
call startTask-2   Thread-2
call startTask-1   Thread-1
call startTask-4   Thread-4
call startTask-3   Thread-3
call startTask-6   Thread-6
call startTask-5   Thread-5
call startTask-7   Thread-7
call startTask-8   Thread-8
call startTask-9   Thread-9
call endTask-4   Thread-4
call endTask-6   Thread-6
call endTask-5   Thread-5
call endTask-3   Thread-3
call endTask-1   Thread-1
call endTask-0   Thread-0
call endTask-2   Thread-2
call endTask-8   Thread-8
call endTask-9   Thread-9
call endTask-7   Thread-7
从结果上看,虽然task2 在task1前面但是task2的thread是thread-2;task1 的是thread-1这说明task1先分配线程执行,只是输出在后面。

SynchronousQueue保证先进入队列的出队列(先被执行


怎么理解“SynchronousQueue在某次添加元素后必须等待其他线程取走后才能继续添加”呢?一个任务加入队列就分配线程,然后再让下一个任务进入队列,以此类推,好像一个管道



0 0
原创粉丝点击