线程池使用小结

简介: Executors 返回的线程池有着无法避免的劣势。使用线程池强制使用 ThreadPoolExecutor 创建,建议小伙伴在对线程池的机制有充分的了解的前提下使用 。 当然使用 ThreadPoolExecutor创建线程池的原因还有: 1、根据机器的性能、业务场景来手动配置线程池的参数比如核心线程数、使用的任务队列、拒绝策略等等。 2、显示地给我们的线程池命名,这样有助于定位问题。 3、方便开发人员对线程池运行状况进行监测,方便及时调整策略避免生产问题。

     既然线程池是日常工作非常常见的知识且使用过程中需要对此有着充分的认知,所以今天就总结一下线程池的常见知识点。

lQLPDhrgNVM_FsDNAZDNBUSwFg0dOYbsIO0BnJXu0gBUAA_1348_400.png

如上图:阿里巴巴 Java 开发手册中对于线程池的创建有着明确的规范。


简单的例子

如下使用 ThreadPoolExecutor实现了自定义线程池完成 Callable的任务:

classImpCallableimplementsCallable<String> {
// 核心线程数privatestaticfinalintCORE_POOL_SIZE=2;
// 最大线程数privatestaticfinalintMAX_POOL_SIZE=4;
// 线程数大于 corePoolSize 线程持续时间privatestaticfinalintKEEP_ALIVE_TIME=1;
// 阻塞队列的大小privatestaticfinalintQUEUE_CAPACITY=5;
privatestaticfinalTimeUnitUNIT=TimeUnit.SECONDS;
// 自定义线程名privatestaticfinalStringTHREAD_NAME="my-self-thread-pool-%d";
publicstaticvoidmain(String[] args) throwsExecutionException, InterruptedException {
ThreadPoolExecutorexecutor=newThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME,
UNIT,
newArrayBlockingQueue<>(QUEUE_CAPACITY),
newBasicThreadFactory.Builder().namingPattern(THREAD_NAME).build(),
newThreadPoolExecutor.CallerRunsPolicy());
ImpCallabletask=newImpCallable();
List<Future<String>>futureList=newArrayList<>();
for (inti=0; i<10; i++) {
// 提交任务到线程池Future<String>future=executor.submit(task);
// 任务结果 future 加入结果队列futureList.add(future);
        }
for (Future<String>fut : futureList) {
try {
// 取出结果System.out.println(newDate() +"--"+fut.get());
            } catch (InterruptedException|ExecutionExceptione) {
e.printStackTrace();
            }
        }
executor.shutdown();
try {
executor.awaitTermination(5, TimeUnit.SECONDS);
        } catch (InterruptedExceptione) {
e.printStackTrace();
        }
System.out.println("All threads Finished");
    }
@OverridepublicStringcall() throwsException {
Thread.sleep(2000L);
returnThread.currentThread().getName();
    }
}


阻塞队列

用来保存等待被执行的任务的阻塞队列,Java 中提供了如下阻塞队列:

  1. ArrayBlockingQueue:基于数组结构的有界阻塞队列,其构造必须指定大小。对象按 FIFO 排序。
  2. LinkedBlockingQuene:基于链表结构的阻塞队列,大小不固定,若不指定大小,其大小有Integer.MAX_VALUE 来决定。对象按 FIFO 排序。吞吐量通常要高于 ArrayBlockingQuene
  3. SynchronousQuene:特殊的 BlockingQueue,对其的操作必须是放和取交替完成。
  4. priorityBlockingQuene:类似于 LinkedBlockingQueue,对象的排序由对象的自然顺序或者构造函数的 Comparator决定。


自定义线程名

创建线程的工厂,通过自定义的线程工厂可以给每个新建的线程设置一个具有识别度的线程名。

关于这一点非常有必要,方便快速定位问题以及监控线程池。


拒绝策略

线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:

  1. AbortPolicy直接抛出异常,默认策略,可以及时发现线程池的瓶颈。
  2. CallerRunsPolicy使用调用者所在的线程来执行任务,新任务不会丢失,采用谁提交谁负责的策略,有效控制线程池压力。
  3. DiscardOldestPolicy丢弃阻塞队列中靠最前的任务,并执行当前任务,该策略存在丢失任务的风险,不建议使用
  4. DiscardPolicy直接丢弃任务,该策略简单粗暴以保证系统可以为主要目标,不建议使用

当然也可以根据应用场景实现 RejectedExecutionHandler接口,自定义饱和策略。如记录日志或持久化存储不能处理的任务。


线程池大小

关于线程池线程大小可以根据实际业务场景具体设置。

推荐个适用面比较广的公式( N 为 CPU 核心数):

  • CPU 密集型任务 N+1 。
  • IO 密集型任务 2N 。


线程池状态

lQLPDhrgNkB5EojNAZbNBAmw3Jz-wb2rGjIBnJd0EcBaAA_1033_406.png

如上图线程池的状态分为五种,分别对应 Java 中五个 int 字段:

privatestaticfinalintRUNNING=-536870912;
privatestaticfinalintSHUTDOWN=0;
privatestaticfinalintSTOP=536870912;
privatestaticfinalintTIDYING=1073741824;
privatestaticfinalintTERMINATED=1610612736;
  • RUNNING线程创建成功初始化状态,能够接收新任务,以及对已添加的任务进行处理。
  • SHUTDOWN不接收新任务,但能处理已添加的任务。
  • STOP不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
  • TIDYING当所有的任务已终止队列任务也为空线程池会变为 TIDYING状态。
  • TERMINATED线程池彻底终止,由 TIDYING状态变成 TERMINATED状态。


监控线程池

我们可以通过第三方组件监控线程池的运行状态比如 SpringBoot 中的 Actuator 。


除此之外,我们还可以利用 ThreadPoolExecutor的相关 API 监测线程池状态。如下图我们可以轻松获得线程池的各项参数,结合邮件实时监测线程池健康状况。


lQLPDhrgNqQDP6vNAfbNBAqwKf64oXyh_DQBnJgW4YBBAA_1034_502.png



相关文章
|
6月前
|
Cloud Native Ubuntu Linux
云原生
Docker是一个基于Go语言的开源容器化平台,实现“一次镜像,处处运行”。它通过容器技术将应用及其依赖打包,实现高效、轻量的部署与隔离,相比传统虚拟机启动更快、资源占用更少。
369 0
云原生
|
JSON 网络协议 数据格式
Docker(35)- docker inspect 命令详解
Docker(35)- docker inspect 命令详解
1684 0
Docker(35)- docker inspect 命令详解
|
机器学习/深度学习 算法 PyTorch
Pytorch自动求导机制详解
在深度学习中,我们通常需要训练一个模型来最小化损失函数。这个过程可以通过梯度下降等优化算法来实现。梯度是函数在某一点上的变化率,可以告诉我们如何调整模型的参数以使损失函数最小化。自动求导是一种计算梯度的技术,它允许我们在定义模型时不需要手动推导梯度计算公式。PyTorch 提供了自动求导的功能,使得梯度的计算变得非常简单和高效。
701 0
|
6月前
|
人工智能 IDE 数据可视化
携手中国科学院青年科学家,我们上线了这款AI科研助手
中国科学院大学他山协会推出AI科研助手「他山科研IDE」,已登陆阿里云云工开物平台及云市场。该工具支持文献检索、数据分析、论文撰写等全流程,助力科研自动化。同期,“Agent for Science”实训营将于12月启动,提供系统课程与前沿讲座,由阿里云提供模型与算力支持,推动AI赋能科研创新。
386 0
|
开发者 索引
HarmonyOS使用系统图标
HarmonyOS图标符号是系统内置的图标资源库,开发者可通过SymbolGlyph和SymbolSpan组件高效引用图标资源,简化开发流程并确保应用与系统设计风格一致。通过`$r(&#39;sys.symbol.resource_name&#39;)`访问系统图标资源,支持调整大小、颜色、粗细、渲染策略及动效。更多示例和学习资料详见官方文档和教程。
1026 2
HarmonyOS使用系统图标
|
移动开发 网络协议 前端开发
每日一博 - Server-Sent Events推送技术
每日一博 - Server-Sent Events推送技术
874 0
|
存储 安全 Linux
s3fs挂载S3对象桶
s3fs(Simple Storage Service File System)是一个基于FUSE(Filesystem in Userspace)的文件系统,它允许将S3(Simple Storage Service)或其他兼容S3 API的对象存储服务挂载到本地文件系统中,从而能够像访问本地磁盘一样访问远程对象存储。以下是通过s3fs挂载OBS(Object Storage Service,对象存储服务,这里以华为云OBS为例)对象桶的基本步骤: ### 一、环境准备 1. **安装s3fs**: - 对于CentOS系统,可以使用yum安装s3fs-fuse: ```
3316 7
|
存储 前端开发 JavaScript
毕业设计|基于SpringBoot+VUE的开源云盘系统
毕业设计|基于SpringBoot+VUE的开源云盘系统
428 1
毕业设计|基于SpringBoot+VUE的开源云盘系统
|
消息中间件 JSON 自然语言处理
Python多进程日志以及分布式日志的实现方式
python日志模块logging支持多线程,但是在多进程下写入日志文件容易出现下面的问题: PermissionError: [WinError 32] 另一个程序正在使用此文件,进程无法访问。 也就是日志文件被占用的情况,原因是多个进程的文件handler对日志文件进行操作产生的。
|
并行计算 Ubuntu Docker
ubuntu安装Nvidia-docker2详细步骤
ubuntu安装Nvidia-docker2详细步骤
3973 0