多线程与高并发(九):线程池

1、简介

1.1、 线程池的概念

线程池是线程的集合,它会初始化一些线程,当有任务提交给线程池时,线程池会分配空闲的线程去执行任务,执行结束之后,线程会再次成为空闲状态,等待执行下一个任务。

1.2、 线程池的优点

  • 降低资源消耗。通过重复利用已创建的线程,降低线程创建和销毁造成的消耗。
  • 提高响应速度。当任务到达时,任务可以不需要等待线程创建就能立即执行。
  • 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控。
  • 提供更丰富的功能,例如延时执行、定时执行等。

1.3、线程池的工作方式

线程池做的工作主要是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其它线程执行完毕,再从队列中取出任务来执行。


2、几种常用线程池

2.1、Executor

  • 接口定义: 该接口是线程池的最上层的父接口,此接口表示执行提交的可运行任务的对象。此接口提供了一种将任务提交与如何运行每个任务的机制分离的方法,负责线程使用、调度等。实现此接口的子类对象需要用执行器而不是显式创建线程的方法去异步执行任务。

2.2、ExecutorService

  • 接口定义: 该接口是Executor的子接口,对Executor进行了扩展,它提供了对于线程池生命周期的管理和追踪和执行任务进行管理的功能,包括了管理线程池的生命周期、追踪任务的执行进度和结果等。真正的线程池是在ExecutorService的基础上进行实现的。

2.3、ThreadPoolExecutor

2.3.1、继承关系:

ThreadPoolExecutor继承关系
ThreadPoolExecutor 继承了 AbstractExecutorService,而 AbstractExecutorService 实现了 ExecutorService 接口,故 ThreadPoolExecutor 是一个线程池真正的实现类。

2.3.2、ThreadPoolExecutor的7个参数

  • corePoolSoze: 核心线程数,当初始化线程池是,线程池里会初始化一定数量的线程,这些线程为线程池的核心线程,它们的生命周期与线程池的生命周期相同。
  • maximumPoolSize: 最大线程数,当核心线程用完时,线程池能额外创建一些线程,这个参数表示线程池能够拥有的最大线程数值,最大线程数 = 核心线程数 + 最多可扩展线程数。
  • keepAliveTime: 生存时间,当额外扩展的线程超过这个时间后,扩展的线程会被销毁。
  • unit: 生存时间单位。
  • workQueue: 任务队列,当没有空闲线程后,任务会暂时先放置在任务队列中,当线程空闲出来时,再依次执行任务队列中的任务。
  • threadFactory: 线程工厂,需实现ThreadFactory接口,用于生产线程池中的线程。线程的优先级、group、是否为守护线程、线程名称等均在该工厂中进行指定。
  • 拒绝策略: 指的是当队列装满时,且并且工作线程已经达到最大线程数时,需要以什么样的策略拒绝任务。jdk默认提供了4中拒绝策略:
    ① ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
    ② ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃。
    ③ ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交任务。
    ④ ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务。

2.3.2、如何获取一个ThreadPoolExecutor

  • 通过Executors获取: Executors是一个线程池的工厂类,它提供了线程池的创建等方法

① Executors.newFixedThreadPool(int): 获取一个固定线程数量的线程池。
通过源码可以看到,newFixedThreadPool 获取的是一个不可扩展、任务队列无限长、使用默认工厂方法生成线程、拒绝策略为 AbortPolicy 的线程池。

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

Executors.newSingleThreadExecutor(): 获取只有一个线程数量的线程池。
通过源码可以看到,newSingleThreadExecutor 获取的是一个只有一个线程、任务队列无限长、使用默认工厂方法生成线程、拒绝策略为 AbortPolicy 的线程池。

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

Executors.newCachedThreadPool(): 获取一个可扩容的线程池。
通过源码可以看到,newCachedThreadPool 获取的是一个只有没有核心线程、线程池最大线程数为MAX_VALUE、扩展线程生存时间为60秒、任务队列长度为1、使用默认工厂方法生成线程、拒绝策略为 AbortPolicy 的线程池。

public static ExecutorService newCachedThreadPool() {
	return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
								  60L, TimeUnit.SECONDS,
								  new SynchronousQueue<Runnable>(),
								  Executors.defaultThreadFactory(), new AbortPolicy());
}
  • 自定义一个ThreadPoolExecutor:
    通过调用ThreadPoolExecutor构造方法,传入7个参数自定义一个线程池。

-线程池如何取最大线程数:

CPU密集型: CPU核心数 + 1或2
CPU密集型也是指计算密集型,大部分时间用来做计算逻辑判断等CPU动作的程序称为CPU密集型任务。该类型的任务需要进行大量的计算,主要消耗CPU资源。 这种计算密集型任务虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低,所以,要最高效地利用CPU,计算密集型任务同时进行的数量应当约等于CPU的核心数。

IO密集型: CPU核数 / 阻塞系数(通常取2倍cpu核数)
IO密集型任务指任务需要执行大量的IO操作,涉及到网络、磁盘IO操作,对CPU消耗较少,其消耗的主要资源为IO。

  • 注:
    在这里插入图片描述

2.3.3、线程池处理任务的流程

在这里插入图片描述
在这里插入图片描述

  • 在创建了线程池之后,开始等待请求。
  • 当调用execute()方法添加一个任务请求时,线程池会作出如下判断:
    ① 如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务。
    ② 如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列。
    ③ 如果这个时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程,并立即运行这个任务
    ④ 如果队列满了而且正在运行的线程数量≥maximumPoolSize,那么线程池会启动饱和拒绝策略来执行。
  • 当一个线程完成任务时,他会从队列中取下一个任务来执行。
  • 当一个线程无事可做超过一定时间(keepAliveTime)时,线程会判断:
    ① 如果当前运行的线程数大于corePoolSize,那么这个线程就会被停掉
    ② 线程池完成所有任务后,它最终会收缩到corePoolSize大小

2.4 ScheduledThreadPoolExecutor

2.4.1 继承关系

在这里插入图片描述

2.4.2 如何使用ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor是定时任务线程池,它继承了ThreadPoolExecutor,它在ThreadPoolExecutor的基础上,扩展了延时执行任务、定时执行任务等功能。

public static void main(String[] args) throws ExecutionException, InterruptedException {
	ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(10);
	// 延时执行任务
	// ScheduledFuture<V> schedule(Callable/Runnable, long, TimeUnit)
	// 参数分别为:任务、延时时间、延时时间单位
	Future<String> future = scheduledPool.schedule(() -> "it's a delay", 1, TimeUnit.SECONDS);
	System.out.println(future.get());
	// 定时执行任务
	// ScheduledFuture<?> scheduleAtFixedRate(Runnable, long, long, unit)
	// 参数分别为:任务、第一次执行延时时间、两次任务执行间隔时间、间隔时间单位
	scheduledPool.scheduleAtFixedRate(() -> System.out.println("it's a Fixed Rate"), 1, 2, TimeUnit.SECONDS);
}

2.5、ForkJoinPool

未完待续

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值