JAVA线程池相关原理

简介: JAVA线程池相关原理

我们都知道,在高并发的请求情况下,很多任务我们可能会采用多线程的方式来处理请求或者消费数据。多线程,顾名思义,很多的线程,如果我们每次都自己创建,然后在销毁线程,很麻烦,很浪费时间,举个不恰当的例子,一个线程做完自己的事后发现同伴还有活没做,完全可以帮着一起做,而不是赶紧下班跑了哈哈,说白了就是自己的事做完了,别着急跑,继续做其他事,让线程复用,来减少创建和销毁的次数,防止创建太多线程导致内存爆满。线程池就是做了这个事。


我们JAVA就提供了线程池,首先我们要明白几个线程池的核心参数和这几个参数实际在运行的时候是怎么协同起来的,最后在介绍下常用的几个线程池。执行过程如图所示:

(一)核心线程数:这个是线程池常备的线程数量,可以理解为,一个池子里基本上有这么多线程在等着干活,一个线程被创建出来,如果,线程数没达到线程池的核心线,每次都是新加的,且不会被释放掉,一直到线程池里线程的数量大于这个核心线程数,才根据空闲时间开始释放掉多余的线程资源,对于他的大小的设置,要区分你这个多线程的任务是,IO密集型还是CPU密集型。

IO密集型:这个任务大部分时间都是操作IO的,比如网络传输,文件读写啥的,这种类型的核心线程数应该我们一般设置成CPU的核数的二倍,为的是,当其他线程在操作IO耗时的时候,其他线程可以尽可能的利用CPU,做到不浪费CPU资源。

CPU密集型:顾名思义,比如计算量特别大的多线程任务,这个时候核心线程数的设置最好和CPU核数保持一致或者+1,为的是减少多个线程在计算的过程中频繁的切换,导致性能的下降。其实最合适的是自己先根据上面规则设置个大概,然后进行压力测试,做到权衡最好的那个适合自己的设置。

(二)任务队列:接着上面的,当一个线程池的核心线程数满了,才开始给队列里存任务,等待着去用核心线程执行,它的大小自己根据任务的多少来设置。

(三)最大线程数:再接着上面,核心线满了,当队列里的任务也满了,这时候看看线程池线程数达没达到最大线程数,没达到就继续创建线程来执行任务直到达到最大线程数。当池子里线程数大于核心线程数的时候,每当有线程空闲时间大于最大空闲时间,这个线程就会被释放掉,直到数量等于核心线程数的时候。

(四)拒绝策略:再接着上面的,当最大线程数也满了咋办?有拒绝策略给了四种选择,还可以自己定制如下:

AbortPolicy满了还提交直接抛异常(RejectedExecutionException)。

CallerRunsPolicy:不抛异常,用触发开启线程的主线程来执行接下来的任务,可能会形成主线程的阻塞。

DiscardPolicy:无为而治,啥也不干,直接把新任务就丢了。

DiscardOldestPolicy:前浪死在沙滩上,把最老进去队列的任务给杀掉,自己加入进去。

定制拒绝策略:implements RejectedExecutionHandler然后重写rejectedExecution方法。


(五)常用线程池

JAVA里提供了四个,三个底层都是直接调用ThreadPoolExecutor的:

newCachedThreadPool:

线程池无限大,比较适合短期的异步任务,负载低的情况,缺点也很明显,看不到具体开启了多少线程,有危险性,核心线程是0,最大线程数integer的最大值(一般没有机器可以到这个境界基本等于无限大),60秒有效期,60秒空闲时间有新的任务进来就用老线程运行,没有就释放掉了。

newFixedThreadPool:一个固定大小的线程池,队列是无限大,负载大的场景根据服务器性能设置线程池大小,源码可以看到,核心线程数和最大线程数相等,存活时间为0,注意这里的0不是0秒的意思,而是正无穷,换句话来说也就是一直存活,再看最后有一个工作队列,LinkedBlockingQueue<Runnable>这个是个正无穷的队列。


newSingleThreadExecutor:

一个单线程的线程池,所有的任务顺序执行和固定大小的区别就是核心线程数和最大线程数都是1。

newScheduledThreadPool:

一个可以延时执行或者重复执行的线程池。DelayedWorkQueue是一个无界队列,它能按一定的顺序对工作队列中的元素进行排列。

此外Spring里我们还常用到ThreadPoolTaskExecutor作为线程池,他其实也是在ThreadPoolExecutor的上面进行了封装源码如下:


最后咱们在贴一个常用的在spring里配置一个线程池的样例:

<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
 3         <!-- 线程池维护线程的最少数量 -->
 4         <property name="corePoolSize" value="5" />
 5         <!-- 允许的空闲时间 -->
 6         <property name="keepAliveSeconds" value="200" />
 7         <!-- 线程池维护线程的最大数量 -->
 8         <property name="maxPoolSize" value="10" />
 9         <!-- 缓存队列 -->
10         <property name="queueCapacity" value="20" />
11         <!-- 对拒绝task的处理策略 -->
12         <property name="rejectedExecutionHandler">
13             <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
14         </property>
15     </bean>


最后,大家想获取更多知识的,可以继续关注公众号,不定时推送。分享了这么牛逼的知识,还不请小编喝个水吗,哈哈哈,欢迎土豪直接赏赞,谢谢,您的支持就是小编最大的动力。

相关文章
|
1月前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
|
1月前
|
存储 Java 关系型数据库
高效连接之道:Java连接池原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。频繁创建和关闭连接会消耗大量资源,导致性能瓶颈。为此,Java连接池技术通过复用连接,实现高效、稳定的数据库连接管理。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接池的基本操作、配置和使用方法,以及在电商应用中的具体应用示例。
74 5
|
2月前
|
存储 算法 Java
Java HashSet:底层工作原理与实现机制
本文介绍了Java中HashSet的工作原理,包括其基于HashMap实现的底层机制。通过示例代码展示了HashSet如何添加元素,并解析了add方法的具体过程,包括计算hash值、处理碰撞及扩容机制。
|
1天前
|
监控 Java API
探索Java NIO:究竟在哪些领域能大显身手?揭秘原理、应用场景与官方示例代码
Java NIO(New IO)自Java SE 1.4引入,提供比传统IO更高效、灵活的操作,支持非阻塞IO和选择器特性,适用于高并发、高吞吐量场景。NIO的核心概念包括通道(Channel)、缓冲区(Buffer)和选择器(Selector),能实现多路复用和异步操作。其应用场景涵盖网络通信、文件操作、进程间通信及数据库操作等。NIO的优势在于提高并发性和性能,简化编程;但学习成本较高,且与传统IO存在不兼容性。尽管如此,NIO在构建高性能框架如Netty、Mina和Jetty中仍广泛应用。
14 3
|
1天前
|
安全 算法 Java
Java CAS原理和应用场景大揭秘:你掌握了吗?
CAS(Compare and Swap)是一种乐观锁机制,通过硬件指令实现原子操作,确保多线程环境下对共享变量的安全访问。它避免了传统互斥锁的性能开销和线程阻塞问题。CAS操作包含三个步骤:获取期望值、比较当前值与期望值是否相等、若相等则更新为新值。CAS广泛应用于高并发场景,如数据库事务、分布式锁、无锁数据结构等,但需注意ABA问题。Java中常用`java.util.concurrent.atomic`包下的类支持CAS操作。
18 2
|
22天前
|
存储 监控 小程序
Java中的线程池优化实践####
本文深入探讨了Java中线程池的工作原理,分析了常见的线程池类型及其适用场景,并通过实际案例展示了如何根据应用需求进行线程池的优化配置。文章首先介绍了线程池的基本概念和核心参数,随后详细阐述了几种常见的线程池实现(如FixedThreadPool、CachedThreadPool、ScheduledThreadPool等)的特点及使用场景。接着,通过一个电商系统订单处理的实际案例,分析了线程池参数设置不当导致的性能问题,并提出了相应的优化策略。最终,总结了线程池优化的最佳实践,旨在帮助开发者更好地利用Java线程池提升应用性能和稳定性。 ####
|
1月前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
1月前
|
Java
Java之CountDownLatch原理浅析
本文介绍了Java并发工具类`CountDownLatch`的使用方法、原理及其与`Thread.join()`的区别。`CountDownLatch`通过构造函数接收一个整数参数作为计数器,调用`countDown`方法减少计数,`await`方法会阻塞当前线程,直到计数为零。文章还详细解析了其内部机制,包括初始化、`countDown`和`await`方法的工作原理,并给出了一个游戏加载场景的示例代码。
Java之CountDownLatch原理浅析
|
19天前
|
监控 Java 开发者
深入理解Java中的线程池实现原理及其性能优化####
本文旨在揭示Java中线程池的核心工作机制,通过剖析其背后的设计思想与实现细节,为读者提供一份详尽的线程池性能优化指南。不同于传统的技术教程,本文将采用一种互动式探索的方式,带领大家从理论到实践,逐步揭开线程池高效管理线程资源的奥秘。无论你是Java并发编程的初学者,还是寻求性能调优技巧的资深开发者,都能在本文中找到有价值的内容。 ####
|
1月前
|
Java 索引 容器
Java ArrayList扩容的原理
Java 的 `ArrayList` 是基于数组实现的动态集合。初始时,`ArrayList` 底层创建一个空数组 `elementData`,并设置 `size` 为 0。当首次添加元素时,会调用 `grow` 方法将数组扩容至默认容量 10。之后每次添加元素时,如果当前数组已满,则会再次调用 `grow` 方法进行扩容。扩容规则为:首次扩容至 10,后续扩容至原数组长度的 1.5 倍或根据实际需求扩容。例如,当需要一次性添加 100 个元素时,会直接扩容至 110 而不是 15。
Java ArrayList扩容的原理