java线程池

简介: java线程池

1. 什么是线程池

线程池简单理解就是一个池子,里面存放着已经创建好的线程,当有任务提交到线程池里面来的时候,池子中的某个线程就会主动去执行该任务,当提交过来的任务比较多,池子中的线程数不够用时,会自动扩充新的线程到线程池子,最多扩充到配置的最大线程个数,而当任务比较少时,池子中的线程个数就自动回收,把线程资源释放掉;并且通常情况下,为了能缓存提交过来的未被处理的任务,就需要有一个任务队列来存放,这就是一个线程池。

2.为什么要有线程池?

我们知道创建和销毁一个对象是很费时间的,特别是一些比较耗费资源的对象的创建和销毁,比如创建数据库连接,创建网络连接,创建线程等,所以就出现“池化技术”,即复用已经创建的对象,那么这样做能够带来 3 个好处:

(1)降低资源消耗,通过复用已经创建的线程降低线程创建和销毁造成的系统资源消耗;

(2)提高性能,当执行大量异步任务时线程池能够提供更好的性能,在不使用线程池时,每

当需要执行异步任务时直接 new 一个线程来运行,而线程的创建和销毁是需要开销的,而线

程池里面的线程是可复用的,不需要每次执行异步任务时都重新创建和销毁线程,直接执行任

务即可;

(3)方便线程管理,线程是不能随随便滥用的,当不停地创建线程可能导致系统资源消耗殆

尽而崩溃,使用线程池可以限制创建的线程个数、动态新增线程数量等,提高了线程的可管理

3.Java 线程池之 Executor 框架

(1)为了实现线程池和管理线程池,JDK 给我们提供了基于 Executor 接口的一系列接口、抽象类、实现类,我们把它称作线程池的 Executor 框架,Executor 框架本质上是一个线程池;

(2)Java 线程(java.lang.Thread)被一对一映射为本地操作系统内核线程,Java 线程启动时会创建一个本地操作系统线程,操作系统会调度所有线程并将它们分配给可用的 CPU 执行,当该 Java 线程终止时,这个操作系统线程也会被回收;实际上这是两层线程调度模型:

①上层 Java 线程的调度由 Executor 框架调度;

②下层操作系统的线程调度由操作系统调度;

(3)Java 的线程是这么设计的,包含两部分:

①工作任务;(Runnable 和 Callable)

②执行机制;(Thread、Executor 框架)

3.1 Executor 框架的接口与类结构

java.util.concurrent (并发编程的工具) juc

java.util.concurrent.atomic (变量的线程安全的原子性操作)

java.util.concurrent.locks (用于锁定和条件等待同步等)

①Executor:顶级接口,提交任务

②ExecutorService:扩展了Executor接口,提供了submit等api

③ForkJoinPool:工作窃取,补充之前的线程池

④ThreadPoolExecutor:自定义线程池的时候使用

⑤java.util.concurrent.Executors: 有静态方法,可以创建线程池,返回线程池对象

3.2 线程池的 7 大参数

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

(1)int corePoolSize, 线程池中的核心线程数量,即线程池中维护的最小的线程数量,即使这些线程处于空闲状态,它们也不会被销毁,除非设置了 allowCoreThreadTimeOut;默认情况下,创建线程池之后,线程池中是没有线程的,需要提交任务之后才会创建线程;在实际中如果需要线程池创建之后立即创建线程,可以通过以下两种方式:

①boolean prestartCoreThread(),初始化一个核心线程;

private ThreadPoolExecutor executor = new ThreadPoolExecutor(100,300,1000, 
  TimeUnit.MILLISECONDS,new ArrayBlockingQueue<>(50), 
  new ThreadFactoryBuilder().setNamePrefix("Line-Control-").build(),
  new ThreadPoolExecutor.DiscardOldestPolicy());
 boolean b = executor.prestartCoreThread();

②int prestartAllCoreThreads(),初始化所有核心线程;

(2)BlockingQueue<Runnable> workQueue, 任务队列,当核心线程全部繁忙时,提交的 Runnable 任务存放到该任务队列中,等待被核心线程来执行

(3)int maximumPoolSize, 线程池中允许的最大线程数,当核心线程全部繁忙且任务队列存满之后,线程池会临时追加线程,直到总线程数达到 maximumPoolSize 这个上限;

(4)long keepAliveTime, 线程空闲超时时间,如果一个线程处于空闲状态,并且当前的线程数量大于 corePoolSize,

那么在指定时间后,这个空闲线程会被销毁;

(5)TimeUnit unit

keepAliveTime 的时间单位 (天、小时、分、秒…)

(6)ThreadFactory threadFactory, 线程工厂,用于创建线程,一般采用默认的Executors.defaultThreadFactory()即可,也可以自定义实现

(7)RejectedExecutionHandler handler, 拒绝策略(饱和策略),当任务太多来不及处理时,就需要进行任务拒绝。任务拒绝是线程池的保护措施,当核心线程 corePoolSize 正在执行任务、线程池的任务队列

workQueue 已满、并且线程池中的线程数达到 maximumPoolSize 时,就需要“拒绝”掉新提交

过来的任务;

JDK 提供了 4 种内置的拒绝策略:AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy 和

DiscardPolicy;

①AbortPolicy(默认):丢弃任务并抛出 RejectedExecutionException 异常,这是线程池默认

的拒绝策略,在任务不能再提交的时候抛出异常,让开发人员及时知道程序运行状态,这样能

在系统不能承载更大的并发量时,及时通过异常信息发现;

②DiscardPolicy:直接丢弃任务,不抛出异常,使用此策略可能会使我们无法发现系统的异

常状态,建议一些无关紧要的业务采用此策略;

③DiscardOldestPolicy:丢弃任务队列中靠最前的任务,并执行当前任务,是否要采用此拒绝

策略,根据实际业务是否允许丢弃老任务来评估和衡量;

④CallerRunsPolicy: 交由任务的调用线程(提交任务的线程,如main线程)来执行当前任务;这种拒绝策

略会让所有任务都能得到执行,适合大量计算类型的任务执行,使用这种策略的最终目标是要让每个任务都能执行完毕,而使用多线程执行计算任务只是作为增大吞吐量的手段;

除了上面的四种拒绝策略,还可以通过实现 RejectedExecutionHandler 接口,实现自定义的拒绝策略

3.3 ExecutorService接口常用方法

(1)void shutdown() 启动一次顺序关闭,执行以前提交的任务,不能再提交新的任务了。

(2)List<Runnable> shutdownNow() 停止所有正在执行的任务,暂停处理正在等待的任务,并返回等待执行的任务列表。

(3)<T> Future<T> submit(Callable<T> task) 执行带返回值的任务,返回一个Future对象。

(4)Future<?> submit(Runnable task) 执行 Runnable 任务,并返回一个表示该任务的 Future。

(5)<T> Future<T> submit(Runnable task, T result) 执行 Runnable 任务,并返回一个表示该任务的 Future。

4. 线程池状态

ThreadPoolExecutor 使用 int 的高 3 位来表示线程池状态,低 29 位表示线程数量

5.JDK内置线程池ExecutorService实现类

获取ExecutorService可以利用JDK中的Executors 类中的静态方法,常用获取方式如下:

5.1 newFixedThreadPool

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

①核心线程数 == 最大线程数(没有救急线程被创建),因此也无需超时时间

②阻塞队列是无界的,可以放任意数量的任务

③适用于任务量已知,相对耗时的任务

5.2 newCachedThreadPool

创建一个默认的线程池对象,里面的线程可重用,且在第一次使用时才创建

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

5.3 newSingleThreadExecutor

创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程

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

(1)使用场景:希望多个任务排队执行。线程数固定为 1,任务数多于 1 时,会放入无界队列排队。任务执行完毕,这唯一的线程也不会被释放。

(2)自己创建的一个单线程和newSingleThreadExecutor区别

自己创建的一个单线程串行执行任务,如果任务执行失败而终止那么没有任何补救措施(任务队列中的其他任务将无法执行),而线程池还会新建一个线程,保证池的正常工作

(3)newFixedThreadPool(1)和newSingleThreadExecutor的区别

①Executors.newSingleThreadExecutor() 线程个数始终为1,不能修改;FinalizableDelegatedExecutorService 应用的是装饰器模式,只对外暴露了 ExecutorService 接口,因此不能调用 ThreadPoolExecutor 中特有的方法

②Executors.newFixedThreadPool(1) 初始时为1,以后还可以修改;对外暴露的是 ThreadPoolExecutor 对象,可以强转后调用 setCorePoolSize 等方法进行修改

6. 线程池工作流程

目录
相关文章
|
18天前
|
监控 Java 调度
Java线程池ThreadPoolExecutor初略探索
Java线程池ThreadPoolExecutor初略探索
|
10天前
|
Java
解析Java线程池:参数详解与执行流程
解析Java线程池:参数详解与执行流程
10 1
|
24天前
|
缓存 NoSQL Java
Java高并发实战:利用线程池和Redis实现高效数据入库
Java高并发实战:利用线程池和Redis实现高效数据入库
48 0
|
26天前
|
存储 SQL 监控
JAVA 线程池的分析和使用
JAVA 线程池的分析和使用
19 0
|
4天前
|
Java 调度
Java中的线程池机制详解
Java中的线程池机制详解
|
11天前
|
缓存 并行计算 Java
重温JAVA线程池精髓:Executor、ExecutorService及Executors的源码剖析与应用指南
重温JAVA线程池精髓:Executor、ExecutorService及Executors的源码剖析与应用指南
|
11天前
|
Java
java线程池执行任务(一次任务、固定间隔时间任务等)
java线程池执行任务(一次任务、固定间隔时间任务等)
8 1
|
11天前
|
监控 Java 调度
Java并发编程:深入理解线程池
【6月更文挑战第26天】在Java并发编程的世界中,线程池是提升应用性能、优化资源管理的关键组件。本文将深入探讨线程池的内部机制,从核心概念到实际应用,揭示如何有效利用线程池来处理并发任务,同时避免常见的陷阱和错误实践。通过实例分析,我们将了解线程池配置的策略和对性能的影响,以及如何监控和维护线程池的健康状况。
10 1
|
19小时前
|
存储 Java 程序员
解析Java中的线程池的工作原理
解析Java中的线程池的工作原理
|
3天前
|
缓存 Prometheus 监控
Java中的线程池优化与调度策略
Java中的线程池优化与调度策略