java线程池TreadPoolExecutor的使用

一、线程池作用

线程池能够对线程进行统一分配,调优和监控:

降低资源消耗(线程无限制地创建,然后使用完毕后销毁)

提高响应速度(无须创建线程)

提高线程的可管理性

二、原理

线程集合workSet和阻塞队列workQueue,新添加的线程放入阻塞队列,workerSet中的一个个线程worker会从阻塞队列中拿线程来执行,如果阻塞队列中没有线程,worker会阻塞。

worker没达到核心线程数之前,来一个任务创一个worker,也就是线程

三、七大参数

1、构造

public ThreadPoolExecutor(int corePoolSize,                         //核心线程数
                          int maximumPoolSize,                      //最大线程数
                          long keepAliveTime,                       //超过核心线程数以外的线程的允许空闲时间
                          TimeUnit unit,                            //空闲时间单位
                          BlockingQueue<Runnable> workQueue,        //阻塞队列对象
                          ThreadFactory threadFactory,              //线程工厂,给线程起名字用的,没啥用
                          RejectedExecutionHandler handler)         //线程数达到最大核心线程数,且阻塞队列已满时的拒绝策略

2、参数设置原则

从任务的优先级,任务的执行时间长短,任务的性质(CPU密集/ IO密集),任务的依赖关系这四个角度来分析。

并且近可能地使用有界的工作队列。

性质不同的任务可用使用不同规模的线程池分开处理:

CPU密集型: 尽可能少的线程,Ncpu+1

IO密集型: 尽可能多的线程, Ncpu*2,比如数据库连接池

混合型: CPU密集型的任务与IO密集型任务的执行时间差别较小,拆分为两个线程池;否则没有必要拆分。

四、实用的三种线程池

1、newFixedThreadPool

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

可以看到,核心线程数和最大线程数一致,那允许空闲时间就失效了,因为不会有超过核心线程数的线程存在

没有设置拒绝策略,默认为AbortPolicy,会抛出RejectedExecutionException

但即便设置了拒绝策略也无效,因为阻塞队列是无界的,最大Integer.MAX_VALUE,不会满的

2、newSingleTreadPool

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

可以看到,只有一个线程在运行,同样keepalivetime和拒绝策略失效

3、newCachedTreadPool

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                    60L, TimeUnit.SECONDS,
                                    new SynchronousQueue<Runnable>());
}

可以看到,没有核心线程数,一旦线程空闲超过60秒,就会停止,下次再建新的线程,会导致一定的系统开销

五、应该自己new线程池

通过参数看到,上面三个常用的线程池,都会造成OOM问题,要么阻塞队列无限长,要么线程数无限多

所以自己new线程池比较适合自己的业务

new ThreadPoolExecutor(5, 200, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

六、ScheduledThreadPoolExecutor

继承自ThreadPoolExecutor,为任务提供延迟或周期执行,属于线程池的一种。

特性:

使用专门的任务类型—ScheduledFutureTask 来执行周期任务,也可以接收不需要时间调度的任务(这些任务通过 ExecutorService 来执行)

使用专门的存储队列—DelayedWorkQueue 来存储任务,DelayedWorkQueue 是无界延迟队列DelayQueue 的一种。相比ThreadPoolExecutor也简化了执行机制(DelayedWorkQueue内部只允许存储 RunnableScheduledFuture 类型的任务。与 DelayQueue 的不同之处就是它只允许存放 RunnableScheduledFuture 对象,并且自己实现了二叉堆)

支持可选的run-after-shutdown参数,在池被关闭(shutdown)之后支持可选的逻辑来决定是否继续运行周期或延迟任务。并且当任务(重新)提交操作与 shutdown 操作重叠时,复查逻辑也不相同。

七、还可以通过spring bean的方式创建线程池

八、提交线程

用submit()方法,提交一个线程或future对象

九、记得用完后关闭线程池

shutdown()   shutdownNow()

执行这两个方法后,isShutdown返回true,全部线程终止后,isTerminated返回true

十、阻塞队列

BlockingQueue为单端队列接口,一端写入,一端读取,当队列为空时,读会阻塞,属于典型的生产者-消费者模式

实现类有:ArrayBlockingQueue、DelayQueue、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue

数组阻塞队列、延迟队列(每个元素会在一个固定时间后才可以被消费)、链式阻塞队列、有优先级的阻塞队列(元素必须实现java.lang.Comparable接口)、同步队列(其实队列只能容纳一个元素,再想写会阻塞)

十一、拒绝策略

1、AbortPolicy

拒绝时抛出异常RejectedExecutionException,是默认的拒绝策略

2、CallerRunsPolicy

只要线程池没有shutdown,立即运行这个线程

3、DiscardPolicy

什么都不做,即直接丢弃

4、DiscardOldestPolicy

丢弃阻塞队列中最老的线程,运行这个线程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值