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>


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

相关文章
|
3天前
|
存储 算法 Java
Java HashSet:底层工作原理与实现机制
本文介绍了Java中HashSet的工作原理,包括其基于HashMap实现的底层机制。通过示例代码展示了HashSet如何添加元素,并解析了add方法的具体过程,包括计算hash值、处理碰撞及扩容机制。
|
1月前
|
算法 Java
JAVA并发编程系列(8)CountDownLatch核心原理
面试中的编程题目“模拟拼团”,我们通过使用CountDownLatch来实现多线程条件下的拼团逻辑。此外,深入解析了CountDownLatch的核心原理及其内部实现机制,特别是`await()`方法的具体工作流程。通过详细分析源码与内部结构,帮助读者更好地理解并发编程的关键概念。
|
2天前
|
开发框架 Java 程序员
揭开Java反射的神秘面纱:从原理到实战应用!
本文介绍了Java反射的基本概念、原理及应用场景。反射允许程序在运行时动态获取类的信息并操作其属性和方法,广泛应用于开发框架、动态代理和自定义注解等领域。通过反射,可以实现更灵活的代码设计,但也需注意其性能开销。
10 1
|
13天前
|
算法 Java 开发者
Java中的垃圾回收机制:从原理到实践
Java的垃圾回收机制(Garbage Collection, GC)是其语言设计中的一大亮点,它为开发者提供了自动内存管理的功能,大大减少了内存泄漏和指针错误等问题。本文将深入探讨Java GC的工作原理、不同垃圾收集器的种类及它们各自的优缺点,并结合实际案例展示如何调优Java应用的垃圾回收性能,旨在帮助读者更好地理解和有效利用Java的这一特性。
|
19天前
|
网络协议 安全 Java
Java Socket原理
Java Socket原理是指在Java中通过Socket实现的网络通信的基础理论与机制。Socket是网络中不同设备间通信的一种标准方式,它允许应用程序之间通过TCP/IP等协议进行数据交换。在Java中,利用Socket编程可以方便地创建客户端与服务器端应用,实现跨网络的数据传输功能,是互联网软件开发中的重要技术之一。它支持多种通信模式,如可靠的流式套接字(TCP)和数据报式套接字(UDP)。
|
15天前
|
前端开发 Java API
JAVA Web 服务及底层框架原理
【10月更文挑战第1天】Java Web 服务是基于 Java 编程语言用于开发分布式网络应用程序的一种技术。它通常运行在 Web 服务器上,并通过 HTTP 协议与客户端进行通信。
14 1
|
1月前
|
Java
JAVA并发编程系列(9)CyclicBarrier循环屏障原理分析
本文介绍了拼多多面试中的模拟拼团问题,通过使用 `CyclicBarrier` 实现了多人拼团成功后提交订单并支付的功能。与之前的 `CountDownLatch` 方法不同,`CyclicBarrier` 能够确保所有线程到达屏障点后继续执行,并且屏障可重复使用。文章详细解析了 `CyclicBarrier` 的核心原理及使用方法,并通过代码示例展示了其工作流程。最后,文章还提供了 `CyclicBarrier` 的源码分析,帮助读者深入理解其实现机制。
|
27天前
|
安全 Java 编译器
Java反射的原理
Java 反射是一种强大的特性,允许程序在运行时动态加载、查询和操作类及其成员。通过 `java.lang.reflect` 包中的类,可以获取类的信息并调用其方法。反射基于类加载器和 `Class` 对象,可通过类名、`getClass()` 或 `loadClass()` 获取 `Class` 对象。反射可用来获取构造函数、方法和字段,并动态创建实例、调用方法和访问字段。虽然提供灵活性,但反射会增加性能开销,应谨慎使用。常见应用场景包括框架开发、动态代理、注解处理和测试框架。
|
1月前
|
Java
Java的aop是如何实现的?原理是什么?
Java的aop是如何实现的?原理是什么?
20 4
|
1月前
|
存储 Java
JAVA并发编程AQS原理剖析
很多小朋友面试时候,面试官考察并发编程部分,都会被问:说一下AQS原理。面对并发编程基础和面试经验,专栏采用通俗简洁无废话无八股文方式,已陆续梳理分享了《一文看懂全部锁机制》、《JUC包之CAS原理》、《volatile核心原理》、《synchronized全能王的原理》,希望可以帮到大家巩固相关核心技术原理。今天我们聊聊AQS....