Java线程池是个什么东西?核心原理又是什么?

如果觉得我的文章还不错的话就点个赞,关注一波,转发收藏吧,另外可以微信搜索【佘凡架构师】阅读更多的好文章,获取我为大家准备的资料。

背景

最近有好多同学都在问,面试官为什么那么喜欢问线程池,blabla连环炮一大堆,我们应该怎么应对面试话的连环夺命追击问?

我们知道但凡是一个系统,那么就不会让这个系统无限制的创建很多很多的线程,那么分分钟就OOM,肯定会构建一个线程池,有一定数量的线程,让他们执行各种各样的任务,线程执行完毕任务之后,不会让线程销毁掉自己,继续去执行下一个任务。这样避免线程的创建,初始化,销毁的各种系统资源成本。

什么是线程池?

简单来讲线程池就是一个池子,里面装有多个线程。有点像我们生活中的游泳池,首先我们会给游泳池的长宽高设置好体积,然后当我们要游泳的时候就往里面注入水。当我们游泳完毕后,水不要了就会做释放。

那线程池也是如此,当我们有任务需要的时候,这样就会生成核心线程处理任务。
那么问题来了,我们的任务太多,核心线程处理不过来怎么办?不用担心,我们有阻塞队列,将任务放入阻塞队列中,等待处理。
那问题又来了,阻塞队列都已经放满了任务,这个时候怎么办?没关系,我们继续生成非核心线程去处理阻塞队列中的任务。
完了完了,连非核心线程都被使用完毕,这个时候应该怎么办?没关系,我们还有终极大招,拒绝策略。

让我们画个图理解一下吧

  1. 首先我们大池子(线程池),这个线程池里默认只能装3个线程。现在里面是空的,因为没有任务可以执行,所以核心线程还没初始化。
    在这里插入图片描述

  2. 现在进来了3个任务,所以创建了3个核心线程去执行任务
    在这里插入图片描述

  3. 这个时候突然又出现了3个任务,但是线程池中的核心线程已经满了,所以没办法,只能放入阻塞队列。
    在这里插入图片描述

  4. 当任务1执行完毕以后,任务4就会被现在空闲的核心线程a继续执行在这里插入图片描述

  5. 这个时候突然又来了3个任务(任务7,任务8,任务9),而阻塞队列中只能放3个任务。并且线程池中的核心线程都还没有执行手头的任务。这个时候我们有非核心线程(假设2个),就会创建非核心线程来处理剩余的任务(处理任务8,任务9)。在这里插入图片描述

  6. 假如又有了2个任务(任务10,任务11),这个时候所有的线程还是没有把手头的任务执行完毕。我们就会执行拒绝策略在这里插入图片描述

  7. 恰好这个时候突然执行力度增强,一下子线程池中的任务全部执行完毕。那么线程池中的线程会从阻塞队列中取出任务来执行。在这里插入图片描述

  8. 之后再也没有任务进入到阻塞队列中,那么非核心线程会开始等待空闲时间。一旦超过空闲时间,那么非核心线程就会自动销毁。最终只有3个核心线程存在在这里插入图片描述

线程池的核心参数有哪些?

从上面的那几个图我们了解到,线程池有核心线程,非核心线程,阻塞队列,非核心线程的空闲时间,拒绝策略。那让我们来看一下线程池的核心参数代码吧。

public ThreadPoolExecutor(int corePoolSize, //核心线程数
                              int maximumPoolSize,//最大线程数=核心线程数+非核心线程数
                              long keepAliveTime,//空闲时间(非核心线程无任务空闲时间)
                              TimeUnit unit,//空闲单位
                              BlockingQueue<Runnable> workQueue //阻塞队列
                              ) { 
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory() //默认线程工厂
             , defaultHandler //默认拒绝策略(AbortPolicy)
             );
    }

线程池的拒绝策略有哪些?

在这里插入图片描述
CallerRunsPolicy : 直接调用当前线程执行任务策略
AbortPolicy :直接抛异常
DiscardPolicy:丢弃当前任务策略
DiscardOldestPolicy:丢掉最后一个阻塞队列中的任务,尝试把当前任务放进去
还有一种自定义策略,实现RejectedExecutionHandler接口。一般我们都是使用这种策略,保证任务的不丢失。

线程池有哪几种创建方式?

  1. 使用Executors,Executors是Java的一个工具类,其中有4种创建线程的方式。

newFixedThreadPool(固定线程池):Executors.newFixedThreadPool(nThreads)
缺点:LinkedBlockingQueue是无界队列,当任务太多,而线程太少,任务处理不过来的时候。太多的任务放在无界队列种容易导致OOM。

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

newSingleThreadExecutor(单个线程池):Executors.newSingleThreadExecutor()。核心线程与最大线程都是1
缺点:LinkedBlockingQueue是无界队列,当任务太多,而线程太少,任务处理不过来的时候。太多的任务放在无界队列种容易导致OOM。

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

newCachedThreadPool(缓存线程池):Executors.newCachedThreadPool()
缺点:最大线程数为Integer.MAX_VALUE,当任务过多,而阻塞队列SynchronousQueue容量为0。创建太多的非核心线程,导致OOM。

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

ScheduledThreadPoolExecutor(定时线程池):Executors.ScheduledThreadPoolExecutor(corePoolSize)
缺点:自定义了核心线程数,但是最大线程数依旧为Integer.MAX_VALUE。

public ScheduledThreadPoolExecutor(int corePoolSize) {
   super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}
  1. 自定义核心参数,创建线程池
    在这里插入图片描述

生产环境突然宕机,线程池中的请求任务怎么办?

一般正常情况就是生产环境突然宕机,线程池中的积压的请求任务当即立即全部丢失,导致线上数据不一致情况。那我们应该怎么解决上述的这个问题?解决方案就是将任务提交到线程池去执行任务时,先在库表里新增一条这样的数据,并且把状态改为未完成。等任务执行完毕以后同步更新库表信息为已完成。使用这样的方式来保证就算生产环境宕机,我们也能够从库表的数据进行回溯。

生产环境使用无界队列会产生什么问题?

生产环境使用无界队列时,若请求任务数不断的增大,会使得当前的阻塞无界队列中不断堆加新的请求任务,直接让内存飙升。更有甚者,直接OOM。

生产环境中队列满了,最大线程数为Integer.MAX_VALUE,会出现什么问题?

同上,生产环境中的阻塞队列满了以后,会不断的创建新的非核心线程来处理任务。它的好处就是当请求任务一下飙升堆积过来之后,能够创建大量的线程快速处理,对并发的请求任务进行削峰处理。缺点就是一旦创建了大量的线程,会使得CPU和内存同时飙升。大量的线程在执行任务时会占用CPU的资源,大量的线程创建会占用内存资源。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值