线程池的底层原理

常用的三种线程池

三种常用线程池的创建方法如下:

import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;

public class ThreadPool {
    public static void main(String[] args){
        //固定线程数目线程池 newFixedThreadPool
        ExecutorService es0 = Executors.newFixedThreadPool(5);

        //可缓存线程池 newCachedThreadPool
        ExecutorService es1 = Executors.newCachedThreadPool();

        //单线程线程池 newSingleThreadExecutor
        ExecutorService es2 = Executors.newSingleThreadExecutor();
    }
}

点开每个线程池,看看他们的源码,可以看出他们都有一个相同的调用ThreadPoolExecutor

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

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

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

我们再查看ThreadPoolExecutor的源码:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
            Executors.defaultThreadFactory(), defaultHandler);
}

发现他有七个参数

线程池的七大参数

  1. corePoolSize:线程池中的常驻核心线程数

  2. maximumPoolSize:线程池中能够容纳同时执行的最大线程数,此值必须大于等于1

  3. keepAliveTime:多余的空闲线程池的存活时间,当前池中线程数超过 corePoolSize 时,当空闲时间达到 keepAliveTime 时,多余线程会被销毁直到只剩下 corePoolSize 个线程为止

  4. unit:keepAliveTime 的单位

  5. workQueue:任务队列,被提交但尚未被执行的任务

  6. threadFactory:表示生成线程池中工作线程的线程工厂,用于创建线程,一般默认即可

  7. handler:拒绝策略,表示队列满时,并且工作线程大于等于 maximumPoolSize 时,如何拒绝请求执行的 runnable 策略

七大参数之间的关系(线程池运行流程)

线程池启动,创建corePoolSize个数量的核心线程,这些线程不管有无任务都会存在,直到线程池被关闭;当任务不断送入线程池,核心线程全部被占用,再有任务被送进来时,存入workQueue任务队列,当有核心线程完成任务时,从任务队列取任务执行;当任务队列满时,线程池会创建一般线程,核心线程数+一般线程数 <= maximumPoolSize,新创建的线程会从任务队列中取任务执行;当线程池中核心线程与一般线程都被占用,且任务队列也满时,线程池会启动handler拒绝策略,阻止线程送入线程池;当任务队列中的任务慢慢被线程取走,任务队列为空,且存在线程为空闲状态,那么这些空闲的线程会等待keepAliveTime存活时间,如果在存活时间内一直处于空闲状态,那么这个线程会被销毁,知道线程池中的线程数目达到 corePoolSize 。

注:上述的 核心线程 与 一般线程 是为了更好的区分 corePoolSize 与 maximumPoolSize ;线程池不会标记哪个线程是核心线程,哪个线程是一般线程,所有的线程都是相同的。

线程池的四大拒绝策略

  • AbortPolicy():抛出一个异常,默认的
  • DiscardPolicy():直接丢弃任务
  • DiscardOldestPolicy():丢弃队列里最老的任务,将当前这个任务继续提交给线程池
  • CallerRunsPolicy():交给线程池调用所在的线程进行处理,即谁提交的任务返回给谁

如何选择线程池

  1. newFixedThreadPool 与 newSingleThreadExecutor:

允许的任务队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,导致OOM

OOM:OutOfMemory,内存溢出,程序申请内存过大,JVM无法满足,导致程序崩溃

  1. newSingleThreadExecutor 与 s.newScheduledThreadPo:

允许创建的线程数目为Integer.MAX_VALUE,可能会创建大量的线程,提高CPU的占用率,导致OOM

阿里巴巴Java开发手册强制要求不允许使用Executors去创建线程池,而是通过ThreadPoolExecutor自行创建线程池。

ExecutorService es = new ThreadPoolExecutor(
        5,                 //corePoolSize
        20,                //maximumPoolSize
        5L,                //keepAliveSize
        TimeUnit.SECONDS,  
        new LinkedBlockingQueue<>(10),
        Executors.defaultThreadFactory(),
        new ThreadPoolExecutor.AbortPolicy());

如上代码是一个自定义的线程池,他的 corePoolSize 等于5; maximumPoolSize 等于 20; keepAliveSize 为5秒;使用长度为10的 LinkedBlockingQueue 阻塞队列;使用默认的线程工厂与 AbortPolicy 拒绝策略。

如何为线程池配置合理的线程数

根据任务的种类分为CPU密集型与IO密集型

CPU密集型

CPU密集型指该任务需要大量的计算,而没有阻塞,CPU一直全速运行;CPU密集型应该尽可能的减少线程数量,以减少线程切换带来的性能损耗,一般配置 CPU核数+1 个线程的线程池

IO密集型

IO密集型指该任务需要大量的IO,即大量的阻塞,使用多线程可以利用被浪费掉的阻塞时间,因此需要多配置线程数,一般配置 CPU核数 /(1 - 阻塞系数) 个线程的线程池,阻塞系数一般为0.8~0.9

以上为个人学习总结,如果疏漏、错误之处,欢迎指出。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值