1. 线程池相关基本概念

  1. 任务(Task):任务是线程池中要执行的工作单元。任务可以是实现了 Runnable 接口或 Callable 接口的对象。Runnable 任务没有返回值,而 Callable 任务可以返回一个结果。

  2. 线程池管理器(ThreadPool Manager):线程池管理器是用于创建和管理线程池的组件。它负责创建线程池,控制线程的创建和销毁,并调度任务的执行。

  3. 工作线程(Worker Threads):工作线程是线程池中实际执行任务的线程。线程池中可以有多个工作线程,它们并行地从任务队列中获取任务并执行。

  4. 任务队列(Task Queue):任务队列是用于存储待执行的任务的数据结构。当线程池中的工作线程空闲时,它们会从任务队列中获取任务并执行。

  5. 拒绝策略(Rejection Policy):拒绝策略定义了当任务队列已满且无法继续接收新的任务时,线程池应该如何处理新的任务。常见的拒绝策略包括丢弃任务、丢弃最早的任务、抛出异常等。

  6. 线程池大小(Pool Size):线程池大小指定了线程池中工作线程的数量。线程池的大小可以是固定的,也可以是根据需要自动调整的。

  7. 核心线程数(Core Pool Size):核心线程数是线程池中保持活动状态的最小工作线程数量。即使线程处于空闲状态,核心线程也不会被销毁。

  8. 最大线程数(Maximum Pool Size):最大线程数是线程池中允许的最大工作线程数量。当任务队列已满且活动线程数达到最大线程数时,线程池可能会创建新的线程来执行任务。

  9. 闲置线程回收时间(Keep-Alive Time):闲置线程回收时间是指当线程池中的线程数超过核心线程数,并且空闲一段时间后,多余的线程会被销毁。

  10. 线程工厂(Thread Factory):线程工厂用于创建线程池中的工作线程。它负责创建线程,并可以自定义线程的属性和命名方式。

线程池的设计目的是提高系统的性能和资源利用率。通过重用线程和控制并发线程的数量,线程池可以减少线程创建和销毁的开销,避免资源耗尽,并提供更好的任务调度和执行控制。

在使用线程池时,我们可以根据任务的类型和系统的需求来选择适当的线程池大小、拒绝策略和其他参数,以实现最佳的性能和可扩展性。

2. 线程池主要处理流程

  • 判断核心线程池是否已满,如果不是,则创建线程执行任务
  • 如果核心线程池满了,判断队列是否满了,如果队列没满,将任务放在队列中
  • 如果队列满了,则判断线程池是否已满,如果没满,创建线程执行任务
  • 如果线程池也满了,则按照拒绝策略对任务进行处理

更进一步的里层核心类处理流程:

当我们使用 Java 中的 ThreadPoolExecutor 类来创建线程池时,其处理流程如下:

  1. 任务提交:外部调用者通过调用 ThreadPoolExecutor 的 execute() 或 submit() 方法将任务提交给线程池。

  2. 任务接收ThreadPoolExecutor 接收到任务后,首先检查线程池的状态。如果线程池已经关闭,就不再接收新的任务。

  3. 任务排队:线程池将接收到的任务放入内部的任务队列中等待执行。任务队列可以是有界队列(如 ArrayBlockingQueue)或无界队列(如 LinkedBlockingQueue 或 SynchronousQueue)。

  4. 工作线程获取任务:线程池中的工作线程从任务队列中获取任务。如果任务队列为空,工作线程可能会阻塞等待新任务的到来,或者在等待一定时间后退出。

  5. 任务执行:工作线程获取到任务后,调用任务对象的 run() 方法执行任务的具体逻辑。

  6. 任务完成:任务执行完成后,可以返回一个结果(对于 Callable 任务),或者不返回任何结果(对于 Runnable 任务)。

  7. 任务状态更新ThreadPoolExecutor 会更新任务的状态,包括任务的执行进度、执行结果等信息。

  8. 结果返回:如果任务是 Callable 任务,ThreadPoolExecutor 会将任务的执行结果封装在 Future 对象中返回给调用者。调用者可以通过 Future 对象获取任务的执行结果。

  9. 继续处理下一个任务:工作线程完成当前任务后,会继续从任务队列中获取下一个任务进行处理。

  10. 线程回收:如果线程池中的线程处于空闲状态,并且空闲时间超过一定阈值,ThreadPoolExecutor 可能会回收这些空闲线程,以避免资源的浪费。

  11. 异常处理ThreadPoolExecutor 会捕获任务执行过程中的异常,并根据预定义的异常处理策略进行处理,比如记录日志、统计异常次数等。

  12. 线程池关闭:当不再需要线程池时,调用 ThreadPoolExecutor 的 shutdown() 或 shutdownNow() 方法来停止线程池的运行。关闭线程池的过程包括不再接收新任务、等待已提交的任务执行完成、销毁工作线程等操作。

ThreadPoolExecutor 是 Java 中用于创建和管理线程池的核心类,通过其灵活的配置参数,可以实现对线程池的各种行为和特性进行定制。这个类提供了丰富的方法和选项,用于控制线程池的大小、任务队列类型、拒绝策略、线程工厂等,以满足不同场景下的需求。

3. 线程池实现的主要步骤

3.1 创建线程池

在 Java 中,可以使用 Executors 类提供的静态方法创建线程池。以下是几种常见的创建线程池的方法:

3.1.1 创建固定大小的线程池

固定大小的线程池将在初始化时创建指定数量的线程,并且不会增加或减少线程的数量。

ExecutorService executorService = Executors.newFixedThreadPool(10);//创建一个固定大小为 10 的线程池
3.1.2 创建单个线程的线程池

单个线程的线程池只会创建一个工作线程来执行任务。

ExecutorService executorService = Executors.newSingleThreadExecutor();
3.1.3 创建可根据需要自动调整大小的线程池

可根据需要自动调整大小的线程池将根据任务的数量动态地增加或减少线程的数量。

ExecutorService executorService = Executors.newCachedThreadPool();
3.1.4 手动按自己需求创建线程池

在 Java 中,可以手动创建线程池,而不仅仅依赖于内置的线程池实现。手动创建线程池的主要原因是为了更好地控制线程池的行为、特性和参数配置,以满足特定的需求,比如特定的任务队列类型需求,特定的拒绝策略需求

根据ThreadPoolExecutor构造方法可知,需要准备以下参数:

  1. 核心线程数(corePoolSize):核心线程数是线程池中保持活动状态的线程数。即使这些线程处于空闲状态,它们也不会被回收。线程池会根据任务的数量和任务队列的状态来动态调整线程池中的线程数量。

  2. 最大线程数(maximumPoolSize):最大线程数指定了线程池中允许存在的最大线程数量。当任务数量超过核心线程数并且任务队列已满时,线程池会创建新的线程来处理任务,直到达到最大线程数。如果达到最大线程数后仍有任务到来,采用拒绝策略处理新任务。

  3. 空闲线程存活时间(keepAliveTime):当线程池中的线程数超过核心线程数,并且处于空闲状态时,空闲线程存活时间指定了它们在没有接收到新任务时的存活时间。超过存活时间后,空闲线程将被回收,直到线程池中的线程数不超过核心线程数。

  4. 时间单位(unit):用于指定时间参数的单位,可以是秒、毫秒、微秒等。

  5. 任务队列(workQueue):任务队列用于存储等待执行的任务。可以选择合适的队列类型,如有界队列(如 ArrayBlockingQueue)或无界队列(如 LinkedBlockingQueue)。

  6. 线程工厂(threadFactory):线程工厂用于创建线程对象。可以自定义线程工厂类,实现创建线程的逻辑。

  7. 拒绝策略(rejectedExecutionHandler):当任务队列已满并且线程池中的线程数达到最大线程数时,拒绝策略指定了如何处理新的任务。可以选择预定义的拒绝策略,如抛出异常、丢弃任务等,或者自定义拒绝策略。

import java.util.concurrent.*;

// 自定义拒绝策略类
class CustomRejectedExecutionHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable runnable, ThreadPoolExecutor executor) {
// 自定义拒绝策略的逻辑
System.out.println("Task Rejected: " + runnable.toString());
// 可根据需求进行不同的处理方式,如抛出异常、丢弃任务、调用者执行等
}
} // 自定义线程工厂类
class CustomThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable runnable) {
// 自定义线程工厂的逻辑
Thread thread = new Thread(runnable);
// 可以进行一些线程属性的配置,如设置线程名称、优先级等
thread.setName("CustomThread");
thread.setPriority(Thread.NORM_PRIORITY);
return thread;
}
} public class ManualThreadPoolCreationExample {
public static void main(String[] args) {
// 创建自定义的拒绝策略实例
RejectedExecutionHandler rejectionHandler = new CustomRejectedExecutionHandler(); // 创建自定义的线程工厂实例
ThreadFactory threadFactory = new CustomThreadFactory(); // 创建任务队列(这里使用无界队列)
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(); // 创建线程池并进行手动配置
int corePoolSize = 10;
int maxPoolSize = 20;
long keepAliveTime = 60;
TimeUnit timeUnit = TimeUnit.SECONDS;
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
keepAliveTime,
timeUnit,
workQueue,
threadFactory,
rejectionHandler
);
}
}

3.2 提交任务给线程池执行

创建线程池之后,可以将任务提交给线程池执行。任务可以是实现了 Runnable 接口的对象,也可以是实现了 Callable 接口的对象。

3.2.1  提交 Runnable 任务

executorService.execute(new Runnable() {
@Override
public void run() {
// 任务逻辑
}
}); 或者使用 Lambda 表达式: executorService.execute(() -> {
// 任务逻辑
});

3.2.2 提交 Callable 任务

Callable 任务可以返回一个结果

Future<SomeResult> future = executorService.submit(new Callable<SomeResult>() {
@Override
public SomeResult call() throws Exception {
// 任务逻辑
return someResult;
}
}); 或者使用 Lambda 表达式: Future<SomeResult> future = executorService.submit(() -> {
// 任务逻辑
return someResult;
});

3.3 关闭线程池

当不再需要线程池时,应该显式地关闭它,以释放资源。关闭线程池两种方式

executorService.shutdown(); // 不再接受新的任务,但会等待已提交的任务执行完成。

executorService.shutdownNow(); //希望立即关闭线程池,并尝试中断正在执行的任务

3.4 处理任务执行结果

当提交任务给线程池执行后,可以通过 Future 对象来获取任务的执行结果。

Future<SomeResult> future = executorService.submit(...);

try {
SomeResult result = future.get();
// 处理结果
} catch (InterruptedException e) {
// 处理中断异常
} catch (ExecutionException e) {
// 处理执行异常
}

使用 future.get() 方法可以阻塞当前线程,直到任务执行完成并返回结果。get() 方法可能会抛出 InterruptedException 和 ExecutionException 异常,需要进行适当的异常处理。

Java线程池使用浅谈的更多相关文章

  1. 浅谈Java线程池的概念、创建与执行

    转': 浅谈Java线程池的概念.创建与执行 如果使用 newCachedThreadPool   线程池的实例: ExecutorService executor = Executors.newCa ...

  2. 干货,阿里P8浅谈对java线程池的理解(面试必备)

    线程池的概念 线程池由任务队列和工作线程组成,它可以重用线程来避免线程创建的开销,在任务过多时通过排队避免创建过多线程来减少系统资源消耗和竞争,确保任务有序完成:ThreadPoolExecutor ...

  3. Java线程池的那些事

    熟悉java多线程的朋友一定十分了解java的线程池,jdk中的核心实现类为java.util.concurrent.ThreadPoolExecutor.大家可能了解到它的原理,甚至看过它的源码:但 ...

  4. Java线程池进阶

    线程池是日常开发中常用的技术,使用也非常简单,不过想使用好线程池也不是件容易的事,开发者需要不断探索底层的实现原理,才能在不同的场景中选择合适的策略,最大程度发挥线程池的作用以及避免踩坑. 一.线程池 ...

  5. Java线程池ThreadPoolExector的源码分析

    前言:线程是我们在学习java过程中非常重要的也是绕不开的一个知识点,它的重要程度可以说是java的核心之一,线程具有不可轻视的作用,对于我们提高程序的运行效率.压榨CPU处理能力.多条线路同时运行等 ...

  6. java的反射机制浅谈(转)

    原文链接:java的反射机制浅谈 一.java的反射机制浅谈 1.何谓反射机制 根据网文,java中的反射机制可以如此定义: JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性 ...

  7. Java 线程池 8 大拒绝策略,面试必问!

    前言 谈到java的线程池最熟悉的莫过于ExecutorService接口了,jdk1.5新增的java.util.concurrent包下的这个api,大大的简化了多线程代码的开发.而不论你用Fix ...

  8. Java 线程池框架核心代码分析--转

    原文地址:http://www.codeceo.com/article/java-thread-pool-kernal.html 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和 ...

  9. Java线程池使用说明

    Java线程池使用说明 转自:http://blog.csdn.net/sd0902/article/details/8395677 一简介 线程的使用在java中占有极其重要的地位,在jdk1.4极 ...

  10. (转载)JAVA线程池管理

    平时的开发中线程是个少不了的东西,比如tomcat里的servlet就是线程,没有线程我们如何提供多用户访问呢?不过很多刚开始接触线程的开发攻城师却在这个上面吃了不少苦头.怎么做一套简便的线程开发模式 ...

随机推荐

  1. 小景的Dba之路--压力测试和Oracle数据库缓存

    小景最近在做系统查询接口的压测相关的工作,其中涉及到了查询接口的数据库缓存相关的内容,在这里做一个汇总和思维发散,顺便简单说下自己的心得: 针对系统的查询接口,首次压测执行的时候TPS较低,平均响应时 ...

  2. 脚踏esbuild祥云,胸怀tsx利刃,身披scss羽衣,追寻前端的本质

    本文所有内容,纯属个人观点,无意与任何人争论 前端技术的现状 我觉得前端技术发展到现在有两个最主要的特征 前端工具链为前端工程化提供了强有力的支持 这方面主要是webpack.rollup.esbui ...

  3. 如何赋予 GPT/LLM 自我意识1

    引子 这个周末OpenAI搞了一个大新闻,围绕 Sam Altman 和 Ilya Sutskever 的各种讨论遍地开花,而其中一个关注点就是他们对于 AGI 降临态度上的偏差.本文不打算讨论公司治 ...

  4. 老知识复盘-SQL从提交到执行到底经历了什么

    一.什么是SQL sql(Structured Query Language: 结构化查询语言)是高级的费过程化编程语言,允许用户在高层数据结构上工作, 是一种数据查询和程序设计语言, 也是(ANSI ...

  5. salesforce零基础学习(一百三十四)State And Country/Territory Picklists启用后的趣事

    本篇参考: https://help.salesforce.com/s/articleView?id=sf.admin_state_country_picklists_overview.htm& ...

  6. MacOS Monterey 配置 PHP 环境记录

    目前 中文网中对于 MacOS 下安装 PHP 教程比较老,并且我个人感觉很难看懂.我在安装 PHP 过程中遇到了很多网络中没有出现过的问题,特此环境配置过程记录如下. 电脑:MacBook Pro ...

  7. java生成企业公章图片源代码

    企业公章图片在电子签章业务中应用广泛,在电子签章应用过程中首先需要生成公章图片,然后再使用公章图片结合数字签名技术完成电子签,这样就实现了从可视化到不可篡改的数字化电子签章功能,以下是企业公章图片生成 ...

  8. k8s 标签-2

    目录 标签-2 node的角色 修改node节点的角色,将他的角色修改成他的主机名 标签的作用 Cordon,Drain以及污点 Cordon--告警警戒 Drain 驱逐演示 污点 污点的Cordo ...

  9. 如何构建一个 NodeJS 影院微服务并使用 Docker 部署

    如何构建一个 NodeJS 影院微服务并使用 Docker 部署 前言 如何构建一个 NodeJS 影院微服务并使用 Docker 部署.在这个系列中,将构建一个 NodeJS 微服务,并使用 Doc ...

  10. 2023-10-28:用go语言,给定一个n*m的二维矩阵,每个位置都是字符, U、D、L、R表示传送带的位置,会被传送到 : 上、下、左、右, . 、O分别表示空地、目标,一定只有一个目标点, 可以

    2023-10-28:用go语言,给定一个n*m的二维矩阵,每个位置都是字符, U.D.L.R表示传送带的位置,会被传送到 : 上.下.左.右, . .O分别表示空地.目标,一定只有一个目标点, 可以 ...