ExecutorService引发的血案(三)ThreadPoolExecutor

2024-05-25 19:38

本文主要是介绍ExecutorService引发的血案(三)ThreadPoolExecutor,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前面我们提到了ExecutorService结构中的一个工厂类,Executors。这个类提供了一系列构造ExecutorService实例的方法。
这些方法的核心就是两个类,分别是 ThreadPoolExecutorScheduledThreadPoolExecutor 类。(当然还有别的类,比较常用的就是这两个)

今天介绍的就是 ThreadPoolExecutor

简介

这个类 的包名 java.util.concurrent.ThreadPoolExecutor 它当然也实现了 ExecutorService 接口

参照官网链接的描述:链接

ExecutorService 在 执行每一个提交的task的时候都是使用线程池内的线程来完成,而且 ExecutorService在配置的时候大部分都是使用Executors提供的方法。

线程池解决了两个不同的问题:
1.他通过(reduced per-task invocation overhead “简化的什么…… 实在不会翻译”)方法,提升了在执行大量异步任务时的性能。
2.他提供了一系列管理资源的方法,包括线程,当在执行异步任务的时候就会开始消耗资源。(当然ThreadPoolExecutor自身也会持有一些静态变量)

在ThreadPoolExecutor的构造方法中,提供了一些参数。这些参数可以根据我们不同的需求来进行对应的配置。 但是官方还是希望我们使用 Executors中提供的工厂方法,这样更加方便,这些提前配置好的方法能够满足大部分的使用场景,但是通过一些配置参数也是可以完成私人定制的。

私人定制

既然是私人定制就不得不提到其中的参数。

int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue< Runnable > workQueue)
Core and maximum pool sizes

corePoolSizemaximumPoolSize

这两个参数相当重要,他的值决定了当一个task执行的时候,thread 是如何分配的。

这里写图片描述

下面这张图大概的介绍了,当一个新task 由 ThreadPoolExecutor执行的时候内部运行的情况。

图中提到的 “根据corePoolSizemaximumPoolSize 判断”,就是根据 running thread 的数量判断,下面我们来分析使用。

(1) 运行状态线程 < corePoolSize
“fewer than corePoolSize threads are running”

当一个新的task通过 `execute`方法提交之后,如果这时 ThreadPoolExecutor中 "运行线程"的数量 满足 条件那么ThreadPoolExecutor会新开线程来执行这个任务,即使存在其他的 闲置线程(idle)没有使用。

(2) 运行状态线程 >= corePoolSize && 运行状态线程 < maximumPoolSize (并且 queue not full )
“more than corePoolSize but less than maximumPoolSize”

如果处于(2)这种条件,那么新的task将会进入queue中,

(3) 在满足(2)的前提下,queue is full (队列已经满了)

如果处于(3)这种条件,那么新的task进来,就会new Thread来执行新的task。

(4)queue is full && 运行状态线程 >=maximumPoolSize

如果处于(4)这种条件,那么新进来 task 直接会被 reject。这个task是无法得到执行的。

(5)

这里其实也要注意一点,那就是Queue的类型,如果Queue是有界的,当然就会出现上面的四种情况。但是如果 Queue是 无界的(不存在full)那么 `运行状态线程`  是永远也不会超过 corePoolSize 的值。
keepAliveTime

这个参数负责当前 ThreadPoolExecutor 管理的 Thread 能够存活的时间。

管理方法:
如果当前线程池中 运行状态线程 >corePoolSize,那么 其他处于 idle状态,并且存活时间 >keepAliveTime 线程就会被 终结。

这个参数在线程池处于不是很活跃的状态的时候可以有效地节约 资源的消耗。

BlockingQueue

这个容器 就是用来存放Task的,一般分为三个种类, 后面的文章会介绍到。

On-demand construction

除了这些构造函数能够初始化以外,还有一些特殊的方法能够进行初始化。

prestartCoreThread()

prestartAllCoreThreads()

工厂方法:

Executors.newCachedThreadPool()

这个方法构建了一个 无界的线程池,它会自动完成线程的回收。

 /*** Creates a thread pool that creates new threads as needed, but* will reuse previously constructed threads when they are* available.  These pools will typically improve the performance* of programs that execute many short-lived asynchronous tasks.* Calls to {@code execute} will reuse previously constructed* threads if available. If no existing thread is available, a new* thread will be created and added to the pool. Threads that have* not been used for sixty seconds are terminated and removed from* the cache. Thus, a pool that remains idle for long enough will* not consume any resources. Note that pools with similar* properties but different details (for example, timeout parameters)* may be created using {@link ThreadPoolExecutor} constructors.** @return the newly created thread pool*/public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());}

我们看出 maximumPoolSize 这个参数给的是 无穷大。至于Queue的选择,后面的文章会讲述Queue。

Executors.newSingleThreadExecutor()

这个方法构建了一个单线程的调度机制

 /*** Creates an Executor that uses a single worker thread operating* off an unbounded queue. (Note however that if this single* thread terminates due to a failure during execution prior to* shutdown, a new one will take its place if needed to execute* subsequent tasks.)  Tasks are guaranteed to execute* sequentially, and no more than one task will be active at any* given time. Unlike the otherwise equivalent* {@code newFixedThreadPool(1)} the returned executor is* guaranteed not to be reconfigurable to use additional threads.** @return the newly created single-threaded Executor*/public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));}
Executors.newFixedThreadPool

这个方法实现了固定线程数量的线程池

  /*** Creates a thread pool that reuses a fixed number of threads* operating off a shared unbounded queue, using the provided* ThreadFactory to create new threads when needed.  At any point,* at most {@code nThreads} threads will be active processing* tasks.  If additional tasks are submitted when all threads are* active, they will wait in the queue until a thread is* available.  If any thread terminates due to a failure during* execution prior to shutdown, a new one will take its place if* needed to execute subsequent tasks.  The threads in the pool will* exist until it is explicitly {@link ExecutorService#shutdown* shutdown}.** @param nThreads the number of threads in the pool* @param threadFactory the factory to use when creating new threads* @return the newly created thread pool* @throws NullPointerException if threadFactory is null* @throws IllegalArgumentException if {@code nThreads <= 0}*/public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),threadFactory);}

可以看到 corePoolSize 和 maximumPoolSize的值是一样的。

这篇关于ExecutorService引发的血案(三)ThreadPoolExecutor的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1002441

相关文章

一道算法题引发的动态内存管理的思考

在做PKU2762时,需要建邻接表。 于是按部就班写了下面一个插入边到邻接表中的函数: const int VMAX = 1010;typedef struct Graph{int vex;Graph* next;}Graph;Graph ArcGraph[VMAX];void insert(int u, int v){Graph* t = new Graph;Graph*

引发蛀牙、避免蛀牙食物大全

引发蛀牙、避免蛀牙食物大全 引发蛀牙的食物大全: 糖果 糖浆 糖果棒 巧克力 碳酸饮料 果汁 口香糖 蜂蜜 蛋糕 甜点 薯片 脆饼干 果酱 果冻 蜜饯 蜜饯果干 避免蛀牙的食物大全: 高纤维蔬菜 水果 坚果 种子 高钙乳制品 高蛋白质肉类 高蛋白质鱼类 绿茶 水 蔬菜汤 鸡汤 酸奶 酸奶制品 奶酪 红薯 土豆 面包和全麦面包 芝士

捉虫笔记(四)-- 空格引发的悬案

空格引发的悬案 1、描述现象: 在代码中有一段利用rmdir指令删除目录代码,但是有用户反馈一直删除失败,但是有没有看到错误的日志信息,正好有同事能复现,所以今天好好探究一番。 2、思考过程 很好奇的一点就是为什么有的环境就是正常。 首先想到2个问题: ①代码有没有执行。 ②假如执行,有没有错误。 关于这两个问题都有个难点,我该如何下断点: 2.1、分析代码是否执行 删除目录的

“苹果税”引发的苹果与腾讯、字节跳动之间的纷争与博弈

北京时间9月10日凌晨一点的Apple特别活动日渐临近,苹果这次将会带来iPhone16系列新品手机及其他硬件产品的更新,包括iPad、Apple Watch、AirPods等。从特别活动的宣传图和宣传标语“閃亮時刻”来看,Apple Intelligence将会是史上首次推出,无疑将会是iOS 18的重头戏和高光时刻。 不过就在9月2日,一则“微信可能不支持iPhone16”的

java线程池ThreadPoolExecutor及四大常用线程池

一、线程池的作用         有时候,系统需要处理非常多的执行时间很短的请求,如果每一个请求都开启一个新线程的话,系统就要不断的进行线程的创建和销毁,有时花在创建和销毁线程上的时间会比线程真正执行的时间还长。而且当线程数量太多时,系统不一定能受得了。                 使用线程池主要为了解决一下几个问题:         a.通过重用线程池中的线程,来减少每个线程创建和销毁的性

Navicat导入时由分号引发的诡异问题

最近在将第三方提供的一个sql导入到自己的数据库的时候,(Event部分的脚本)总是提示错误: [Err] 1064 - You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near

由一个 SwiftData “诡异”运行时崩溃而引发的钩深索隐(一)

概述 从 WWDC 23 开始,苹果推出了全新的数据库框架 SwiftData。它借助于 Swift 语言简洁而富有表现力的特点,抛弃了以往数据库所有的额外配置文件,只靠纯代码描述就可以干脆利索的让数据库的创建和增删改查(CRUD)一气呵成。 在本系列博文中,我们将从一个简单而“诡异”的运行“事故”开始,有理有据的深入探寻一番 SwiftData 中耐人寻味的“那些事儿”。 在本

ExecutorService.invokeAny()和ExecutorService.invokeAll()的使用剖析

ExecutorService.invokeAny()和ExecutorService.invokeAll()的使用剖析 时间 2014-07-27 16:15:07   CSDN博客 原文   http://blog.csdn.net/aitangyong/article/details/38172189 主题  Java ExecutorService是JDK并发工

Redis的incr命令引发的反序列化异常和ERR value is not an integer or out of range异常

在Java中使用inc命令的时候发现redis中的值被反序列化后居然不是数字,检查后发现可能是序列化器没对,在redis配置的地方将序列化器设置为 Jackson2JsonRedisSerializer后使用整成,贴上代码 @Bean(name = "RedisTemplate")@SuppressWarnings("all")public RedisTemplate<String,

Failed resolution of: Lcom/growingio/android/sdk/agent/VdsAgent;删除growingio引发的问题

删除了 growingio之后 项目一直报这个错误 Failed resolution of: Lcom/growingio/android/sdk/agent/VdsAgent; 真是讨厌 解决方案 在as 的 Terminal 分别执行这两个命令 ./gradlew cleanBuildCache  ./gradlew clean 如果在使用上面两个命令的时候出现 权限拒绝