线程池相关内容

线程池

线程是重量级对象

  1. 创建与销毁一个线程并不像创建一个对象那么简单,需要调用操作系统内核的 API,整个过程是一个偏重且耗时的操作
  2. 并发的线程数量很多,且执行时间很多的任务就结束,频繁的创建与销毁会大大降低系统效率
  3. 需要一种办法可以对线程进行重复利用,完成任务后并不被销毁,可以继续执行其他任务

线程池的设计思路

  1. 线程池是一种线程的使用模式,它为了降低线程使用过程中,频繁的创建和销毁所带来的资源消耗与代价
  2. 提前创建一定数量的线程,他们时刻准备着,在新任务到达后就开始执行;在完成任务后,再重新回来继续待命
  3. 线程池负责对线程统一分配调优与监控

线程池的优点

  1. 避免了线程的重复创建与开销带来的资源消耗代价
  2. 提升了任务响应速度,任务到达时,直接选一个线程执行而无需等待线程的创建
  3. 提高线程的可管理性,线程的统一分配和管理,也方便统一的监控和调优

线程池的工作原理

  1. 在线程池的内部,我们维护了一个阻塞队列workQueue和一组工作线程,工作线程的个数可以在初始化线程池的时候来指定
  2. 用户提交Runnable任务给线程池,任务会被加入到workQueue中
  3. 线程池内部维护的工作线程会消费workQueue 中的任务并进行执行

如何使用Java 中的线程池

  1. Java提供的线程池相关的工具类Executors中,最核心的是ThreadPoolExecutor
  2. ThreadPoolExecutor的构造需要七大核心参数,定义了线程池的使用功能

线程池的核心参数

ThreadPoolExecutor(
    int corePoolSize 
    int maximumPoolSize 
    long keepAliveTime ·
    TimeUnit unit
    BlockingQueue workQueue ·
    hreadFactory threadFactory 
    RejectedExecutionHandler handler
)

(1)corePoolSize∶核心线程数

corePoolSize表示线程池保有的核心的线程数(最小的线程数)·核心线程会—直存活,即使这些线程处于空闲状态没有任务执行,他们也不会被销毁

  1. 把线程池类比为一个施工队,而线程就是施工队的工人_
  2. 有些时候很闲项目比较少,但是施工队也不能把工人都遣散,至少要留CorePoolSize个人坚守阵地

(2)maximumPoolSize∶线程池最大线程数量

  1. 当项目比较多的时候,施工队就需要增加工人,但是也不能无限制地加
  2. 最多就加到maximumPoolSize个人,当闲下来的时候,施工队就要遣散工人,但是至少保留 corePoolSize个人

(3)keepAliveTime & unit

  1. 上面提到施工队根据忙闲,项目多少来增减工人,那在编程世界里,如何定义忙和闲呢?
  2. 很简单,当线程池内部的线程数已经大于corePoolSize的时候,一个线程如果在一段时间内,都没有执行任务,说明很闲
  3. keepAliveTime和unit 就是用来定义这个"一段时间"的参数。也就是说,如果一个线程空闲了keepAliveTime & unit这么久,那么这个空闲的线程就要被回收了

(4)WorkQueue∶工作队列

新任务被提交到线程池,优先使用核心线程进行执行,核心线程没有空闲的情况下,任务会进入到此工作队列中,任务调度时再从队列中取出任务,由队列应该首先想到的是等待.

(5)threadFactory∶线程工厂

创建一个新线程时使用的工厂,通过这个工厂可以自定义如何创建线程,例如可以给线程指定一个有意义的名字

(6)handler∶ 拒绝策略

如果线程池中所有的线程(最大线程数)都在忙碌,并且工作队列也满了(前提是工作队列患有界队列),那么此时提交任务,线程池就会拒绝接收

至于拒绝的策略,可以通过 handler 这个参数来指定∶

  • CallerRunsPolicy∶提交任务的线程自己去执行该任务。
  • AbortPolicy∶默认的拒绝策略,直接丢弃任务,抛出RejectedExecutionException。
  • DiscardPolicy∶直接丢弃任务,没有任何异常抛出。
  • DiscardOldestPolicy∶丢弃最老的任务,其实就是把最早进入工作队列的任务丢弃,然后把新任务加入到工作队列。

默认的拒绝策略要慎重使用。如果线程池处理的任务非常重要,建议自定义自己的拒绝策略,并且在实际工作中,自定义的拒绝策略往往和降级策略配合使用

使用线程池,需要注意异常处理的问题。任务在执行的过程中出现运行时异常,会导致执行任务的线程终止,最稳妥和简单的方案还是捕获所有异常并按需处理。

线程池在Java并发编程领域非常重要,很多大厂的编码规范都要求必须通过线程池来管理线程

对于核心参数的设置非常关键

如果在线程池中使用无界阻塞队列,会发生什么?

无界阻塞队列

  • LinkedBlockingQueue默认的最大任务数量是Integer.MAXVALUE
  • 如果线程池内的线程在获取到一个任务后,需要执行时间较长,会导致workQueue里积压的任务越来越多
  • OOM
  • 大量任务的积压导致机器的内存使用不停的飙升,最后导致OOM
  • OOM,"Out Of Memory",俗称"内存用完了"。JVM因为没有足够的内存来为新的对象分配空间,并且垃圾回收器也已经没有空间可回收时,就会抛出这个error(非exception,因为这个问题已经严重到不足以被应用处理.

为什么会OOM?为什么会没有内存了呢?原因主要有两点∶

  • 1)分配的少了∶比如虚拟机本身可使用的内存(一般通过启动时的VM参数指定)太少
  • 2)应用用的太多,用完没释放浪费了,此时就会造成内存泄露或者内存溢出∶内存泄露和内存溢出经常同时出现
  1. 内存泄露∶申请使用完的内存没有释放,导致虚拟机不能再次使用该内存,此时这段内存就泄露了,因为申请者不用了,而又不能被虚拟机分配给别人用
  2. 内存溢出∶申请的内存超出了JVM能提供的内存大小,此时称之为溢出OOM

如果存在远程服务调用,使用无界阻塞队列,可能会出现什么情况?

 只有一个线程的线程池

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
  1. corePoolSize和maxPoolSize设置为1,线程池内只有一个线程
  2. 通过LinkedBlockingQueue将任务进行排队,保证了串行执行所有任务,所有任务肯定是绝对顺序执行

无界阻塞队列不是说一定不能使用,一定要针对不同的场景来进行不同的分析

使用线程池时慎用无界队列,一般情况下针对特定场景才会进行使用

如果线上机器突然宕机,线程池阻塞队列中的请求怎么办?

任务丢失

线程机器宕机,必然导致内存中积压在阻塞队列的任务丢失

解决的思路

任务可能会丢失,怎么办?

  1. 不让它丢-保证系统永远都不会宕机(不切实际)
  2. 丢了之后再找回来

如何保证找回任务

  1. 在提交任务到线程池之前,先任务进行备份
  2. 如果发生宕机,内存中工作队列的任务会消失
  3. 机器重启后,从备份的数据中找回任务,重新提交重新执行

任务分阶段状态:将任务划分为多个状态∶未提交;已提交;已执行

思考下一个场景

如果一个任务在线程池内部执行完毕,还没来得及将任务改为已完成状态,这时候突然宕机,会发生什么?重复执行

任务仍处于已提交状态,重启之后会被重新提交,造成任务的重复执行,解决方案∶

  1. 所以不能只用已提交状态判断,还应该由具体业务,包括幂等性判断,辅助任务的状态判断是否应该重新提交此任务
  2. 如果任务执行的是数据库操作,可以采用事务,保证任务执行和任务状态更新同时完成,或者同时回滚

场景总结

任何时候,都可能会由宕机发生数据丢失∶

  1. 高可用
  2. 数据备份

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值