Java线程池及其使用场景

核心参数/执行原理

7个核心参数,用下图加粗的4个参数结合流程图阐述执行原理。

参数名中文名说明
corePoolSize核心线程数默认不会销毁,设置allowCoreThreadTimeOut为true时会销毁
maximumPoolSize最大线程数核心线程 + 临时/救急线程,大于核心线程数,且不能小于等于0
keepAliveTime空闲时间当超过keepalive时间没有新任务提交,核心线程外的线程会被销毁
unit空闲时间单位KeepAlive的时间单位
workQueue工作队列一种BlockingQueue,JDK实现的queue初始化时需要设置对列长度
threadFactory线程工厂默认提供工厂Executors.DefaultThreadFactory,用于创建线程
handler拒绝策略默认拒绝策略为AbortPolicy,直接抛出异常

workQueue:阻塞队列,没有空闲的核心线程时,新来的任务会加入排队,队列满时会创建临时线程执行任务

handler:所有线程都在忙 + workQueue也放满了,就会触发拒绝策略。

四大拒绝策略:直接抛异常、丢最新的、丢最旧的、用调用者所在线程来执行 。

任务拒绝策略说明
ThreadPoolExcutor.AbortPolicy默认策略,直接抛异常
ThreadPoolExcutor.DiscardPolicy直接丢弃任务,且不会抛出异常,不推荐
ThreadPoolExcutor.DiscardOldestPolciy抛弃队列中等待最久的任务,然后把当前任务加入队列中
ThreadPoolExcutor.CallerRunsPolicy调用任务的run()方法,绕开线程池直接执行

阻塞队列

1.ArrayBlockingQueue:基于 数组 结构的 有界 阻塞 队列,FIFO。

2.LinkedBlockingQueue:基于 链表 结构的 有界 阻塞 队列,FIFO。

3.DelayedWorkQueue :一个 优先级 队列,保证每次出队的任务都是当前队列中执行时间最靠前的。

4.SynchronousQueue:不存储元素的阻塞队列,每个插入操作都必须等待一个移出操作。

LinkedBlockingQueueArrayBlockingQueue
默认无界,支持有界强制有界
底层是链表底层是数组
两把锁(头尾)一把锁

核心线程数的确定

N是CPU的核数。

1.高并发、任务执行时间短——N+1,减少线程上下文切换。

2.并发不高、任务执行时间长

【IO密集型】任务——2N+1

【CPU密集型】任务——N+1

IO密集型任务:文件读写、DB读写、网络请求等,这种情况更常见,利用IO切换的等待时间执行其他线程。

CPU密集型任务:计算型代码、Bitmap转换、Gson转换等

3.高并发、任务执行时间长

先利用缓存增加服务器降低并发,然后参考第二种情况。

线程池的种类

1.创建 使用固定线程数 的线程池 newFixedThreadPool

特点:

  1. 核心线程数与最大线程数一样,没有救急线程
  2. 底层用的LinkedBlockingQueue,最大容量为Integer.MAX_VALUE

适用场景:任务量已知,相对耗时的任务

2.单线程化 的线程池, newSingleThreadExecutor

特点:

  1. 核心线程数和最大线程数都是1
  2. 阻塞队列是LinkedBlockingQueue,最大容量为Integer.MAX_VALUE

适用场景:按照顺序执行的任务

3.可缓存 线程池 newCachedThreadPool

特点:

  1. 核心线程数为0,最大线程数是Integer.MAX_VALUE
  2. 底层用的SynchronousQueue,不存储元素的阻塞队列,每个插入操作都必须等待一个移出操作。

适用场景:任务数比较密集,但每个任务执行时间较短的情况

 4.提供了【延迟】和【周期执行】功能的 ScheduledThreadPoolExecutor

Executors创建线程池的弊端

可能会堆积大量请求或者创建大量线程,导致OOM

1 、 FixedThreadPool 和 SingleThreadPool :

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

2 、 CachedThreadPool :

允许的 创建线程数量 为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM.

使用场景

1.es数据批量导入

使用了线程池+CountDownLatch批量把数据库中的数据导入到了ES(任意)中,避免OOM。

举例:游戏匹配到5个人才能开始。

CountDownLatch:闭锁/倒计时锁,用来进行线程同步协作,等待所有线程完成倒计时。

场景描述:数据50万条,固定每页2000条,计算出总页数为250页,把250作为CountDownLatch的构造参数,即等待250次计数再继续执行。创建任务批量导入es,并且交给线程池执行,主线程内循环创建线程,并调用await方法等待计数归零;每个线程完成一次提交,就执行一次countdown;当计数值为0时,主线程继续执行,并返回提交完成的结果。

2.数据汇总

在一个电商网站中,用户下单之后,需要查询数据,数据包含了三部分:订单信息、包含的商品、物流信息;这三块信息都在不同的微服务中进行实现的,如何完成这个业务?

比如串行执行1800ms,并行执行(利用线程池)800ms,只需等待耗时最久的接口即可。

如果所有接口(或部分接口)的没有依赖关系,就可以使用线程池+future来提升性能。

3.异步调用

比如:关键字搜索的时候,在线程池中获取一个新的线程执行异步保存历史搜索记录。

为了避免下一级方法影响上一级方法(性能考虑),可使用异步线程调用下一个方法(不需要下一级方法返回值),可以提升方法响应时间。

信号量-允许并发访问线程的数量

Semaphore 信号量,底层是AQS,可以通过其限制执行的线程数量。

使用场景:通常用于那些资源有明确访问数量限制的场景,常用于限流

1、创建Semaphore对象,可以传入容量参数

2、acquire()可以请求一个信号量,这时候的信号量个数-1

3、release()释放一个信号量,此时信号量个数+1

一旦没有可使用的信号量,也即信号量个数变为负数时,再次请求的时候就会阻塞,直到其他线程释放了信号量。

ThreadLocal

为每个线程都分配一个独立的线程副本,从而解决了变量并发访问冲突的问题,同时实现了线程内的资源共享。每个线程都可以拥有自己独立的 ThreadLocal 实例,并且可以使用该实例来存储该线程私有的数据,而不会影响其他线程。

1、基本使用:set(value);get();remove();

2、本质上是一个线程内部存储类,让多个线程只操作自己内部的值,从而实现线程数据隔离。每个线程持有一个TreadLocalMap对象,用来存储资源对象:

  • 调用 set 方法,以 ThreadLocal 自己作为 key,资源对象作为 value,放入当前线程的 ThreadLocalMap 集合中。

  • 调用 get 方法,以 ThreadLocal 自己作为 key,到当前线程中查找关联的资源值

  • 调用 remove 方法,以 ThreadLocal 自己作为 key,移除当前线程关联的资源值

3、内存泄漏问题:

  • TreadLocalMap中的key是弱引用,会被GC释放内存;value是强引用,关联value的内存并不会被释放。

  • 建议主动remove释放key,value。

  • 19
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
对于Java线程池的参数配置,可以根据实际需求来确定。以下是一些常见的参数及其推荐配置: 1. 核心线程数(corePoolSize):表示线程池中保持活动状态的线程数量。根据系统的负载情况和处理任务的类型来决定,一般推荐设置为CPU核心数的2倍。 2. 最大线程数(maximumPoolSize):表示线程池允许创建的最大线程数量。根据系统负载和可用资源来决定,一般推荐设置为CPU核心数的2倍或者更大。 3. 空闲线程存活时间(keepAliveTime):表示当线程池中线程数量超过核心线程数时,空闲线程的存活时间。可以根据任务类型和系统负载来调整。如果任务较多且任务执行时间较长,可以适当增大该值。 4. 阻塞队列(workQueue):用于存储等待执行的任务的队列。常见的队列类型有无界队列(如LinkedBlockingQueue)和有界队列(如ArrayBlockingQueue)。根据应用场景和系统负载来选择合适的队列类型。 5. 拒绝策略(rejectedExecutionHandler):表示当任务无法被提交给线程池时的处理策略。例如,默认的ThreadPoolExecutor.AbortPolicy会抛出RejectedExecutionException异常,而ThreadPoolExecutor.CallerRunsPolicy会在提交任务的线程中执行该任务。根据业务需求和系统特点选择合适的拒绝策略。 需要注意的是,线程池参数的配置需要根据具体的业务场景和系统负载来进行调整,以达到最佳性能和资源利用的平衡。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值