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

今天看到关于线程池的一篇帖子,是关于面试时问到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<0
maximumPoolSize<=0 
maximumPoolSize<corePoolSize
keepAliveTime<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比较两次的输出,就会明白了

猜你喜欢

转载自blog.csdn.net/q2365921/article/details/77715888