一、线程池简介
线程池主要是为了控制运行的线程数量,处理过程中将任务放到队列中,然后在线程创建后启动这些任务,如果线程数超过了最大数量,超出数量的线程排队等候,等其它线程执行完毕,再从队列中取出任务来执行。
线程池主要特点是:线程复用、控制最大并发数(削峰操作)、管理线程。
二、如何使用线程池?
Java中的线程池是通过Executor框架实现的,该框架中用到了Executor、Executors、ExecutorsService、ThreadPoolExecutor类。
三个常用的线程池
实现方法描述
Executors.newFixedThreadPool(int)一池n个固定线程Executors.newSingleThreadExecutor()一池单线程Executors.newCachedThreadPool()可扩容线程
(1)Executors.newFixedThreadPool(int)代码实现
package com
.lindaxia
.juc
.demo
;
import java
.util
.concurrent
.ExecutorService
;
import java
.util
.concurrent
.Executors
;
public class MyThreadPoolDemo {
public static void main(String
[] args
) {
System
.out
.println();
ExecutorService threadPool
= Executors
.newFixedThreadPool(3);
try {
for (int i
= 1; i
<=6; i
++) {
threadPool
.execute(
()->{
System
.out
.println(Thread
.currentThread().getName()+"\t号服务员提供服务");
}
);
}
} catch (Exception e
) {
e
.printStackTrace();
} finally {
threadPool
.shutdown();
}
}
}
测试结果
(2)Executors.newSingleThreadExecutor()代码实现
ExecutorService threadPool
= Executors
.newSingleThreadExecutor();
测试结果
(3)Executors.newCachedThreadPool()代码实现
ExecutorService threadPool
= Executors
.newCachedThreadPool();
(1、2、3)方法底层实现源码
三、线程池底层工作原理(非常重要!!!)
(1)7大参数
参数描述
corePoolSize线程池中的常驻核心线程池(new不会创建,Execute执行任务的时候创建,延迟加载,常驻线程池!!!)maximumPoolSize线程池中能容纳同时执行的最大线程数,此值必须>=1;keepAliveTime多余的空闲线程的存活时间,当前池中线程数量超过corePoolSize时,当空闲时间达到keepAliveTime时,多余线程会被销毁直到只剩下corePoolSize个线程为止unitkeepAliveTime的单位workQueue任务队列,被提交但尚未被执行的任务threadFactory生成线程池中工作线程的线程工厂,用于创建线程,一般默认的就行handler拒绝策略,当队列满了,并且工作线程>=线程池的最大线程(maximumPoolSize),拒绝请求执行runnable的策略
java.util.concurrent Class ThreadPoolExecutor 源码
(2)原理流程
①创建线程池后,线程池中的线程数为0【惰性加载】;
②当调用`execute()`方法添加一个请求任务时,线程池会做出如下判断:
如果正在运行的线程数量小于corePoolSize,那么马上创建线 程运行这个任务; 如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列; 如果这个时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务; 如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行。 ③当一个线程完成任务时,它会从队列中取下一个任务来执行。 ④当一个线程空闲、超过一定的时间(keepAliveTime)时,线程会判断:如果当前运行线程数大于corePoolSize,那么这个线程就被停掉。当线程池的所有任务完成后,它最终会收缩到corePoolSize的大小【自动调节池大小】。
四、手写线程池
线程池不允许使用Executors创建,而是通过ThreadPoolExecutor的方式,避免资源耗尽的风险!
FixedThreadPool和singleThreadPool允许请求队列的长度Integer.MAX_VALUE,可能会堆积大量的请求(高并发高访问量)会导致OOM,(LinkedBlockingQueue链表有界阻塞队列的长度2 的 31 次方 - 1 = 2147483648 - 1 = 2147483647,运行程序的内存是有限的,所以提供的三个线程池不要使用,我们手写线程池!
(1)线程池的拒绝策略
等待队列已满,再也塞不下新任务,同时,线程池中的最大线程数也达到极限,这时就无法继续为新任务服务。我们就需要拒绝策略机制合理的处理这个问题!
(2)JDK内置拒绝策略种类
以下策略都实现了RejectedExecutorHandle接口!
拒绝策略描述
AbortPolicy(默认)直接抛出RejectedExecutionException异常阻止系统正常运行CallerRunsPolicy“调用者运行”一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者(main线程),从而降低新任务的流量【吞吐量最大】DiscardOldestPolicy抛弃队列中等待最久的任务,然后把当前任务加人队列中尝试再次提交当前任务【适合时效性的系统】DiscardPolicy该策略默默地丢弃无法处理的任务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的一种策略
(3)手写线程池代码
package com
.lindaxia
.juc
.demo
;
import java
.util
.concurrent
.*
;
public class MyThreadPoolDemo {
public static void main(String
[] args
) {
ExecutorService threadPool
= new ThreadPoolExecutor(
2,
5,
3L
,
TimeUnit
.SECONDS
,
new ArrayBlockingQueue<Runnable>(3),
Executors
.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
try {
for (int i
= 1; i
<= 9; i
++) {
threadPool
.execute(
() -> {
System
.out
.println(Thread
.currentThread().getName() + "\t号服务员提供服务");
}
);
}
} catch (Exception e
) {
e
.printStackTrace();
} finally {
threadPool
.shutdown();
}
}
}
打印结果1
测试结果2
#轻松一刻:
☝上述分享来源个人总结,如果分享对您有帮忙,希望您积极转载;如果您有不同的见解,希望您积极留言,让我们一起探讨,您的鼓励将是我前进道路上一份助力,非常感谢!我会不定时更新相关技术动态,同时我也会不断完善自己,提升技术,希望与君同成长同进步!
☞本人博客:https://coding0110lin.blog.csdn.net/ 欢迎转载,一起技术交流吧!