线程池总结

 1 线程池的构造参数

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

1.1 corePoolSize 核心线程池数量

初始化线程池时默认维护的线程池大小

1.2 maximumPoolSize 最大线程池数量

最大维护的线程池数量, 超过该值时执行拒绝策略

1.5 workQueue : 工作队列

1.5.1 DelayQueue

        无界的队列,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。

1.5.2 LinkedBlockingDeque

        基于双向链表实现的双向并发阻塞队列,该阻塞队列同时支持FIFO和FILO两种操作方式,即可以从队列的头和尾同时操作(添加或删除);并且该阻塞队列是支持线程安全。可以指定队列的容量,如果不指定默认容量大小是Integer.MAX_VALUE

1.5.3 ArrayBlockingQueue

        基于数组实现的有界阻塞队列,此队列按先进先出的原则对元素进行排序。新元素插入到队列的尾部,获取元素的操作则从队列的头部进行。

1.5.4 PriorityBlockingQueue

        带优先级的无界阻塞队列,每次出队都返回优先级最高或者最低的元素(规则可以通过实现Comparable接口自己制定),内部是使用平衡二叉树实现的,遍历不保证有序。

1.6 handler 饱和策略

1.6.1 AbortPolicy

        直接抛出异常,简单粗暴。

1.6.2 CallerRunsPolicy

        在任务被拒绝添加后,会调用当前线程池的所在的线程去执行被拒绝的任务。

1.6.3 DiscardPolicy

        什么都不做,既不抛出异常,也不会执行。

1.6.4 DiscardOldestPolicy

        当任务被拒绝添加时,会抛弃任务队列中最旧的任务(也就是最先加入队列的任务),再把这个新任务添加进去。

2 加入线程执行逻辑

 3 Executors

3.1 newFixedThreadPool : 固定大小线程池

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

        这是创建固定大小的线程池,核心线程数和最大线程数都设置相同的值,使用LinkedBlockingQueue作为工作队列,当corePoolSize满了之后就加入到LinkedBlockingQueue队列中。LinkedBlockingQueue默认大小为Integer.MAX_VALUE,所以会有OOM的风险。

3.2 newSingleThreadExecutor : 线程数为1的线程池

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

        创建线程数为1的线程池,并且使用了LinkedBlockingQueue,核心线程数和最大线程数都为1,满了就放入队列中,执行完了就从队列取一个。也就是创建了一个具有缓冲队列的单线程的线程池。跟上面的问题一样,队列的容量默认是Integer.MAX_VALUE,也会有OOM的风险。

3.3 newCachedThreadPool : 创建可缓冲的线程池

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

        没有大小限制。核心线程数是0,最大线程数是Integer.MAX_VALUE,所以当有新任务时,任务会放入SynchronousQueue队列中,SynchronousQueue只能存放大小为1,所以会立刻新起线程。如果在工作线程在指定时间(60秒)空闲,则会自动终止。

3.4 开发规范

在阿里java开发规范中,是强制不允许使用Executors创建线程池,我们不妨看看。

4 线程池容量的设置

最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目

我们的服务器CPU核数为8核,一个任务线程cpu耗时为20ms,线程等待(网络IO、磁盘IO)耗时80ms,那最佳线程数目:( 80 + 20 )/20 * 8 = 40。也就是设置 40个线程数最佳。

4.1 cpu密集型的应用 : 执行的任务大部分时间是在做计算和逻辑判断

       这种情况显然不能设置太多的线程数,否则花在线程之间的切换时间就变多,效率就会变得低下。所以一般这种情况设置线程数为cpu核数+1即可。

4.2 如果是IO密集型的应用: 执行的任务需要执行大量的IO操作

        比如网络IO,磁盘IO,对CPU的使用率较低,因为在IO操作的特点需要等待,那么就可以把CPU切换到其他线程。所以可以设置线程数为CPU核数的两倍+1

5 需要注意的点

5.1 无界队列注意 oom 问题

        core 线程数量满了之后,会像队列中放入待执行任务, 默认队列是无界的, 容量为 int 最大值容量, 当任务执行时间长, 请求量高时, 会不断地像队列中堆积任务导致内存溢出, 所以使用线程池时, 根据自己的业务情况指定队列大小和对应任务拒绝的饱和策略

5.2 线程池共享问题

        我们可以思考这样一个问题, 线程池要所有功能通用的还是每个功能单独初始化一个线程池, 当设置成通用后, 可能因为 a 功能线程执行的速率而影响 b 功能的执行, 可能因为某个功能的性能瓶颈而影响其他等待执行的功能, 所以我们的线程池要做到功能的隔离, 每个功能根据自己的业务特点单独设置线程池容量, 拒绝策

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值