线程池基本知识

本文介绍了使用线程池的原因,包括降低资源消耗、提高响应速度和便于管理。详细阐述了线程池的实现原理,包括任务提交、线程与队列的处理流程,并详细解析了如何通过ThreadPoolExecutor自定义线程池,包括核心线程数、最大线程数、存活时间、等待队列类型和拒绝策略等关键参数。同时,提到了通过Executors创建固定线程池的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、为什么要使用线程池

不使用线程池的坏处

  • 频繁的线程创建和销毁会占用更多的CPU和内存
  • 频繁的线程创建和销毁会对GC产生比较大的压力
  • 线程太多,线程切换带来的开销将不可忽视
  • 线程太少,多核CPU得不到充分利用,是一种浪费

使用线程池的优点

  • 降低资源的消耗。线程本身是一种资源,创建和销毁线程会有CPU开销;创建的线程也会占用一定的内存。
  • 提高任务执行的响应速度。任务执行时,可以不必等到线程创建完之后再执行。
  • 提高线程的可管理性。线程不能无限制地创建,需要进行统一的分配、调优和监控。

二、如何创建线程池

(一)线程池的实现原理

线程池的实现原理
通过上图,可以知道线程池大致的流程:

  • 任务提交后,判断核心线程是否已满,如果不是,则创建线程执行任务
  • 如果核心满了,判断队列是否已满,如果没满,任务放在队列中等待执行
  • 如果队列满了,判断线程池是否已满,如果没满,创建其他线程执行任务,缓解压力
  • 如果线程池也满了,任务没地方放,也无法执行,则根据Handler策略进行处理

(二)如何创建线程池

1、通过ThreadPoolExecutor创建自定义线程池

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

参数含义:

  • corePoolSize:核心线程数,必须有
  • maximumPoolSize:最大线程数,必须有
  • keepAliveTime:线程存活时间,当线程数超过核心线程数时,这些线程的存活时间,就是这些线程多久被销毁,必须有
  • unit:存活时间单位,可以是毫秒、秒、分钟、小时和天,等等,必须有
  • workQueue:等待队列,线程池中的线程数超过核心线程数时,任务将放在等待队列,它是一个BlockingQueue类型的对象,必须有
  • threadFactory:线程工厂,用于创建线程,可有可无
  • handler:线程池满后执行的策略,可有可无
(1)等待队列-workQueue

等待队列是BlockingQueue类型的,理论上只要是它的子类,我们都可以用来作为等待队列。

  • ArrayBlockingQueue:基于数组实现的队列,数组要指定长度,所以是有界队列
  • LinkedBlockingQueue:基于链表实现的队列,使用链表存储数据,默认是一个无界队列;也可以通过构造方法中的capacity设置最大元素数量,所以也可以作为有界队列
  • SynchronousQueue:不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作将一直处于阻塞状态。该队列也是Executors.newCachedThreadPool()的默认队列
  • PriorityBlockingQueue:基于优先级别的阻塞队列,底层基于数组实现,是一个无界队列
  • 延迟队列:其中的元素只有到了其指定的延迟时间,才能够从队列中出队
    等待队列及其子类
(2)拒绝策略-handler

所谓拒绝策略,就是当线程池满了、队列也满了的时候,我们对任务采取的措施。或者丢弃、或者执行、或者其他行为。

JDK常见的4种策略

  • CallerRunsPolicy:在调用者线程执行
  • AbortPolicy:直接抛出RejectedExecutionException异常
  • DiscardPolicy:直接丢弃,不做任何处理
  • DiscardOldestPolicy:丢弃任务里面最先创建的那个任务,再执行当前任务

这四种策略各有优劣,比较常用的是DiscardPolicy,但是这种策略有一个弊端就是任务执行的轨迹不会被记录下来。所以,我们往往需要实现自定义的拒绝策略, 通过实现RejectedExecutionHandler接口的方式。

2、通过Executors直接创建固定线程池

Executors是一个线程工厂,可以固定创建以下线程池

// 创建单一线程的线程池
public static ExecutorService newSingleThreadExecutor();
// 创建固定数量的线程池
public static ExecutorService newFixedThreadPool(int nThreads);
// 创建带缓存的线程池
public static ExecutorService newCachedThreadPool();
// 创建定时调度的线程池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize);
// 创建流式(fork-join)线程池
public static ExecutorService newWorkStealingPool();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值