带你读《2022技术人的百宝黑皮书》——合理使用线程池以及线程变量(7)

简介: 带你读《2022技术人的百宝黑皮书》——合理使用线程池以及线程变量(7)

带你读《2022技术人的百宝黑皮书》——合理使用线程池以及线程变量(6)https://developer.aliyun.com/article/1340063?groupCode=taobaotech

image.png思 考 & 小 结                                                                          

Tomcat中通过自定义任务线程TaskThread实现对每个线程创建时间的记录;使用静态内部类WrappingRunna- ble对Runnable进行包装,用于对StopPooledThreadException异常类型的处理。

 

Tomcat为什么要自定义线程池和任务队列实现?                                                

JUC原生线程池在提交任务时,当工作线程数达到核心线程数后,继续提交任务会尝试将任务放入阻塞队列中, 只有当前运行线程数未达到最大设定值且在任务队列任务满后,才会继续创建新的工作线程来处理任务,因此JUC原生线程池无法满足Tomcat快速响应的诉求。

 

Tomcat为什么使用无界队列?

Tomcat在EndPoint中通过acceptCount和maxConnections两个参数来避免过多请求积压。其中maxCon- nections为Tomcat在任意时刻接收和处理的最大连接数,当Tomcat接收的连接数达到maxConnections时, Acceptor不会读取accept队列中的连接;这时accept队列中的线程会一直阻塞着,直到Tomcat接收的连接数小于maxConnections(maxConnections默认为10000,如果设置为-1,则连接数不受限制)。accept- Count为accept队列的长度,当accept队列中连接的个数达到acceptCount时,即队列满,此时进来的请求一  律被拒绝,默认值是100(基于Tomcat 8.5.43版本)。因此,通过acceptCount和maxConnections两个参数作用后,Tomcat默认的无界任务队列通常不会造成OOM。

/**
*Allows the server developer to specify the acceptCount (backlog) that
*should be used for server sockets. By default, this value
* is 100.
*/
private int acceptCount = 100;
private int maxConnections = 10000;

 

 

最佳实践

 

image.png避免用Executors 的创建线程池

 

 

 

image.png

 

Executors常用方法有以下几个:

 

newCachedThreadPool():创建一个可缓存的线程池,调用 execute 将重用以前构造的线程(如果线程可用)。如果没有可用的线程,则创建一个新线程并添加到线程池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。CachedThreadPool适用于并发执行大量短期耗时短的任务,或者负载较轻的服务器;

newFiexedThreadPool(int nThreads):创建固定数目线程的线程池,线程数小于nThreads时,提交新的任务会创建新的线程,当线程数等于nThreads时,提交新的任务后任务会被加入到阻塞队列,正在执行的线程执行完毕后从队列中取任务执行,FiexedThreadPool适用于负载略重但任务不是特别多的场景,为了合理利用资源,需要限制线程数量;

newSingleThreadExecutor() 创建一个单线程化的 Executor,SingleThreadExecutor适用于串行执行任务的场景,每个任务按顺序执行,不需要并发执行;

newScheduledThreadPool(int corePoolSize) 创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代 Timer 类。ScheduledThreadPool中,返回了一个ScheduledThreadPoolExecutor实例,而ScheduledThreadPoolExecutor实际上继承了ThreadPoolExecutor。从代码中可以看出,Scheduled- ThreadPool基于ThreadPoolExecutor,corePoolSize大小为传入的corePoolSize,maximumPoolSize大小为Integer.MAX_VALUE,超时时间为0,workQueue为DelayedWorkQueue。实际上Scheduled- ThreadPool是一个调度池,其实现了schedule、scheduleAtFixedRate、scheduleWithFixedDelay三个方法,可以实现延迟执行、周期执行等操作;

newSingleThreadScheduledExecutor() 创建一个corePoolSize为1的ScheduledThreadPoolExecutor;

newWorkStealingPool(int parallelism)返回一个ForkJoinPool实例,ForkJoinPool  主要用于实现“分而治之”的算法,适合于计算密集型的任务。

 

 

Executors类看起来功能比较强大、用起来还比较方便,但存在如下弊端:

 

 

FiexedThreadPool和SingleThreadPool任务队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从 而导致OOM;

CachedThreadPool和ScheduledThreadPool允许创建的线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM;

 

使用线程时,可以直接调用 ThreadPoolExecutor 的构造函数来创建线程池,并根据业务实际场景来设置core-

PoolSize、blockingQueue、RejectedExecuteHandler等参数。

 

带你读《2022技术人的百宝黑皮书》——合理使用线程池以及线程变量(8)https://developer.aliyun.com/article/1340061?groupCode=taobaotech

相关文章
|
22天前
|
监控 安全 Java
在 Java 中使用线程池监控以及动态调整线程池时需要注意什么?
【10月更文挑战第22天】在进行线程池的监控和动态调整时,要综合考虑多方面的因素,谨慎操作,以确保线程池能够高效、稳定地运行,满足业务的需求。
100 38
|
20天前
|
Java
线程池内部机制:线程的保活与回收策略
【10月更文挑战第24天】 线程池是现代并发编程中管理线程资源的一种高效机制。它不仅能够复用线程,减少创建和销毁线程的开销,还能有效控制并发线程的数量,提高系统资源的利用率。本文将深入探讨线程池中线程的保活和回收机制,帮助你更好地理解和使用线程池。
44 2
|
22天前
|
Prometheus 监控 Cloud Native
JAVA线程池监控以及动态调整线程池
【10月更文挑战第22天】在 Java 中,线程池的监控和动态调整是非常重要的,它可以帮助我们更好地管理系统资源,提高应用的性能和稳定性。
58 4
|
22天前
|
Prometheus 监控 Cloud Native
在 Java 中,如何使用线程池监控以及动态调整线程池?
【10月更文挑战第22天】线程池的监控和动态调整是一项重要的任务,需要我们结合具体的应用场景和需求,选择合适的方法和策略,以确保线程池始终处于最优状态,提高系统的性能和稳定性。
92 2
|
1月前
|
Java 数据库连接 数据库
不同业务使用同一个线程池发生死锁的技术探讨
【10月更文挑战第6天】在并发编程中,线程池是一种常用的优化手段,用于管理和复用线程资源,减少线程的创建和销毁开销。然而,当多个不同业务场景共用同一个线程池时,可能会引发一系列并发问题,其中死锁就是最为严重的一种。本文将深入探讨不同业务使用同一个线程池发生死锁的原因、影响及解决方案,旨在帮助开发者避免此类陷阱,提升系统的稳定性和可靠性。
48 5
|
1月前
|
Dubbo Java 应用服务中间件
剖析Tomcat线程池与JDK线程池的区别和联系!
剖析Tomcat线程池与JDK线程池的区别和联系!
105 0
剖析Tomcat线程池与JDK线程池的区别和联系!
|
1月前
|
网络协议 安全 Java
难懂,误点!将多线程技术应用于Python的异步事件循环
难懂,误点!将多线程技术应用于Python的异步事件循环
61 0
|
1月前
|
设计模式 Java 物联网
【多线程-从零开始-玖】内核态,用户态,线程池的参数、使用方法详解
【多线程-从零开始-玖】内核态,用户态,线程池的参数、使用方法详解
58 0
|
1月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
43 1
C++ 多线程之初识多线程
|
24天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
19 3