Java线程池(ThreadPoolFactory)构造参数总结

来源:互联网 发布:德国表现主义电影知乎 编辑:程序博客网 时间:2024/05/16 02:26

今天看到关于线程池的一篇帖子,是关于面试时问到ThreadPoolFactory构造器时的一些问题,之前博主也学习过一些关于ThreadPoolFactory构造器的问题,但是一直没有总结过,既然今天有时间,那么就总结一下,避免有些同学走弯路(有些工作多年的老鸟也不一定能准确的说明coreSize,MaxSize,workQueueSize的关系),话不多说上干货。
下面来直接看ThreadPoolFactory最详细的构造器结构

public ThreadPoolExecutor(int corePoolSize,                              int maximumPoolSize,                              long keepAliveTime,                              TimeUnit unit,                              BlockingQueue<Runnable> workQueue,                              ThreadFactory threadFactory,                              RejectedExecutionHandler handler) {        if (corePoolSize < 0 ||            maximumPoolSize <= 0 ||            maximumPoolSize < corePoolSize ||            keepAliveTime < 0)            throw new IllegalArgumentException();        if (workQueue == null || threadFactory == null || handler == null)            throw new NullPointerException();        this.corePoolSize = corePoolSize;        this.maximumPoolSize = maximumPoolSize;        this.workQueue = workQueue;        this.keepAliveTime = unit.toNanos(keepAliveTime);        this.threadFactory = threadFactory;        this.handler = handler;    }

从上面的代码块可以看出来在初始化时,对构造参数做了大小比较,一旦下面比较有一个为真就会抛出异常

corePoolSize<0maximumPoolSize<=0 maximumPoolSize<corePoolSizekeepAliveTime<0

从构造参数的入口可以看出主要的几个参数一共有如下几个
int corePoolSize
int maximumPoolSize
long keepAliveTime
TimeUnit unit
BlockingQueue workQueue
ThreadFactory threadFactory
RejectedExecutionHandler handler
而一般最让人迷糊的3个参数就是corePoolSize,maxmumPoolSize,workQueueSize
下面就针对这几个参数做下总结:
当线程池初始化时,PoolSize为0,当有任务向线程中提交时,线程池开始创建线程,用来执行业务。

一、corePoolSize、maxmumPoolSize、workQueueSize参数关系总结

JobCount代表向线程池中添加的任务数

1.JobCount <=corePoolSize<maxmumPoolSize<workQueueSize
(当JobCount小于等于corePoolSize时,workQueueSize与maxmumPoolSize无效,线程池会创建与JobCount个数一致的线程来执行任务)

2.corePoolSize <JobCount <=workQueueSize <maxmumPoolSize
(当JobCount大于corePoolSize并且小于等于workQueueSize时maxmumPoolSize无效,超出corePoolSize的任务,会在阻塞对列中排队,当corePoolSize中有线程闲置的时候,从workQueue中取出执行)

3.corePoolSize<workQueueSize<JobCount<maxmumPoolSize
(当JobCount大于corePoolSize时并且workQueue中也放满,但JobCount小于maxmumPoolSize时,那么线程池会创建线程来处理超出workQueueSize部分的任务)

4.corePoolSize<workQueueSize+maxmumPoolSize<JobCount
(当JobCount大于corePoolSize并且大于workQueueSize和maxmumPoolSize之和。那么线程池将会调用handler.rejectedExecution方法)


二、RejectedExecutionHandler参数总结

上面说了corePoolSize,maxmumPoolSize,workQueueSize 3个参数的用法了,那么接下来说一下RejectedExecutionHandler这个参数的意思。
从字面上理解为拒绝处理者,可以理解为当任务数大于maxmumPoolSize后的一个回调方法,RejectedExecutionHandler本身是一个接口,jdk本身对他有4个实现类

CallerRunsPolicy //线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度

AbortPolicy(如不指定handler则默认这个) //处理程序遭到拒绝将抛出运行时RejectedExecutionException;

DiscardPolicy //不能执行的任务将被删除

DiscardOldestPolicy //如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)

三、KeepAliveTime、TimeUnit参数总结

TimeUnit参数很好理解,是一个用来当做单位的枚举。主要还是说说keepAliveTime这个参数,网上很多对这个参数都是几句话带过,说是用来控制线程回收时间的参数,确实他的作用就是这样,但是看下面例子

public class Test {    public static void main(String[] args) {        BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1);        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(1, 5, 5, TimeUnit.SECONDS, queue);        System.out.println("pool初始化时PoolSize大小:"+poolExecutor.getPoolSize());        System.out.println("pool初始化时active大小:"+poolExecutor.getActiveCount());        poolExecutor.execute(new Test().new Task("1"));        poolExecutor.execute(new Test().new Task("2"));        poolExecutor.execute(new Test().new Task("3"));        poolExecutor.execute(new Test().new Task("4"));        try {            Thread.sleep(8000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("pool现在PoolSize大小:"+poolExecutor.getPoolSize());        System.out.println("pool现在active大小:"+poolExecutor.getActiveCount());        // poolExecutor.shutdown();    }    class Task implements Runnable {        String name;        public Task(String name) {            this.name = name;        }        @Override        public void run() {            int i=0;            while (true) {                i++;                if (name.equals("4")) {                    if (i==5) {                        break;                    }                }                if (name.equals("2")) {                    if (i==2) {                        break;                    }                }                System.out.println(name);                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }    }}

例子看上去不是太明白,但是keepAliveTime本身就不是很容易触发
1.keepAliveTime和maximumPoolSize及BlockingQueue的类型均有关系。如果BlockingQueue是无界的,那么永远不会触发maximumPoolSize,自然keepAliveTime也就没有了意义

2.反之,如果核心数较小,有界BlockingQueue数值又较小,同时keepAliveTime又设的很小,如果任务频繁,那么系统就会频繁的申请回收线程。
上面的例子总结起来就是
1.假设corePoolSize=1,QueueSize=1,MaxmumPoolSize=5
2.当初始化线程池的时候CorePoolSize为0,假设这个时候来了4个任务为t1、t2、t3、t4
3.那么线程池会创建一个线程用来执行t1
4.接下来发现coreSize不足,那么将t2放到Queue中
5.又发现QueuePool的大小不够了,判断MaxSize大小没有超出,那么接下来申请线程执行t3和t4
6.,执行了一会发现t4执行完毕了,(PS:注意了)这个时候不管keepaliveTime不管设置的多小,也不会回收的,因为QueuPool中还有一个t2没有执行,那么现在就会拿刚才用来执行t4的线程执行t2,执行的时候发现t2执行的很快,那么现在线程池的状态就是一共有3个线程,两个属于active的,那么这个时候就靠keepAliveTime参数判断回收时间了。

可以将TimeUnit的单位修改为NANOSECONDS比较两次的输出,就会明白了

原创粉丝点击