为了账号安全,请及时绑定邮箱和手机立即绑定

Java并发——线程池ThreadPoolExecutor

标签:
Java

线程池作用

相对于为每个请求都创建一个线程,线程池通过重用现有的线程而不是创建新线程,可以在处理多个请求时分摊在线程创建和销毁过程中产生的巨大开销,当请求到达时,工作线程通过已经存在,不会由于等待创建线程而延迟任务的执行,从而提高响应性。通过适当调整线程池的大小,可以创建足够多的线程以便使处理器保持忙碌状态,同时还可以防止过多线程相互竞争资源而使应用程序耗尽内存或失败

线程池处理流程

1)判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则进入下个流程

2)判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程

3)判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务

示意图:

webp



创建线程池

ThreadPoolExecutor构造方法:

    public ThreadPoolExecutor(int corePoolSize,

                              int maximumPoolSize,

                              long keepAliveTime,

                              TimeUnit unit,

                              BlockingQueue workQueue,

                              ThreadFactory threadFactory,

                              RejectedExecutionHandler handler) {

        ...    //代码省略

    }

一共七个参数:

corePoolSize

线程池中的核心线程数,当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使有其他空闲的核心线程能够执行新任务也会创建线程,直到线程数等于corePoolSize就不再创建,继续提交的任务被保存到阻塞队列中。如果调用了线程池的prestartAllCoreThreads()或者prestartAllCoreThreads()方法,线程池会提前创建并启动所有核心线程

maximumPoolSize

线程池最大线程数,如果当前阻塞队列满了,继续提交任务,若当前线程数小于maximumPoolSize则创建新的线程执行任务。注意如果使用了无界的阻塞队列这个参数就没什么效果

keepAliveTime

线程空闲时保持存活时间,即当线程没有任务执行时,继续存活的时间。若当前线程池的线程数超过corePoolSize,且线程空闲时间超过keepAliveTime,就将这些空闲线程销毁,尽可能降低资源销毁

unit

keepAliveTime的时间单位,可以是天、小时、分、毫秒、微秒和纳秒

workQueue

用于保存等待执行的任务的阻塞队列

threadFactory

创建线程的工厂,可以通过线程工厂给每个创建出来的线程设 置更有意义的名字

handler

线程池的饱和策略(或者叫拒绝策略),当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。Java线程池提供了以下4种策略:

①.AbortPolicy:直接抛出异常,默认策略

②.CallerRunsPolicy:只用调用者所在线程来运行任务

③.DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务

④.DiscardPolicy:不处理,直接丢弃

也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略

调用Exectors中的静态工厂方法也可以来创建线程池

newFixedThreadPool

    public static ExecutorService newFixedThreadPool(int nThreads) {

        return new ThreadPoolExecutor(nThreads, nThreads,

                                      0L, TimeUnit.MILLISECONDS,

                                      new LinkedBlockingQueue());

    }

复制代码

创建一个固定长度的线程池,每当提交一个任务时就创建一个线程,直到达到线程池的最大数量(corePoolSize == maximumPoolSize),这时线程池的规模将不再变化(若某个线程由于发生了未预期的Exception而结束,线程池会补充一个新线程),使用LinkedBlockingQuene作为阻塞队列,适用于负载比较重的服务器

newCachedThreadPool

    public static ExecutorService newCachedThreadPool() {

        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,

                                      60L, TimeUnit.SECONDS,

                                      new SynchronousQueue());

    }

创建一个可缓存线程的线程池,默认缓存60s,使用SynchronousQueue作为阻塞队列(没有数据缓存空间的阻塞队列,每一个put操作必须等待一个take操作,若任务提交的速度远远大于CachedThreadPool的处理速度,CachedThreadPool会不断地创建新线程来执行任务,可能会导致系统耗尽CPU和内存资源)。适用于执行很多的短期异步任务的小程序,或者负载较轻的服务器,使用该线程池时,一定要注意控制并发的任务数,否则创建大量的线程可能导致严重的性能问题

newSingleThreadExecutor

    public static ExecutorService newSingleThreadExecutor() {

        return new FinalizableDelegatedExecutorService

            (new ThreadPoolExecutor(1, 1,

                                    0L, TimeUnit.MILLISECONDS,

                                    new LinkedBlockingQueue()));

    }

单线程的Executor,线程池中只有一个线程,若线程异常结束,会创建另一个线程替代。newSingleThreadExecutor能确保依照任务在队列中的顺讯来串行执行,内部使用LinkedBlockingQueue作为阻塞队列,适用于需要保证顺序地执行各个任务;并且在任意时间点,不会有多个线程是活动的应用场景



作者:Java大生
链接:https://www.jianshu.com/p/1d23c20d3bcd


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
205
获赞与收藏
1007

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消