线程池
为了更好地实现用户级的线程调度,更有效地帮助开发人员进行多线程开发,Java 提供了一套 Executor 框架。这个框架中包括了 ScheduledThreadPoolExecutor 和 ThreadPoolExecutor 两个核心线程池。前者是用来定时执行任务,后者是用来执行被提交的任务
为什么要用线程池
1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。
Executors 线程池工具类
Executors 可以类比于 Arrays 和 Collections,Executors 类中包含了一些静态方法,它们负责生成各种类型的线程池 型ExecutorService 实例。
类型 | 特性 |
---|---|
newFixedThreadPool | 固定大小的线程池,当有新的任务提交,线程池如果有空闲线程则立即执行,否则新任务会进入等待队列 |
newSingleThreadExecutor | 只用一个线程来执行任务,可以保证任务有序执行 |
newCachedThreadPool | 线程池大小不固定,可灵活回收线程,若无可回收,则创建新先线程 |
newScheduledThreadPool | 定时线程池,支持定时及周期性任务执行 |
线程池原理
Executors 利用工厂模式实现的四种线程池,进入四种工厂类后,我们可以发现除了 newScheduledThreadPool 类,其它类均使用了 ThreadPoolExecutor 类进行实现,如下是其构造方法:
public ThreadPoolExecutor(int corePoolSize,//线程池的核心线程数量
int maximumPoolSize,//线程池的最大线程数
long keepAliveTime,//当线程数大于核心线程数时,多余的空闲线程存活的最长时间
TimeUnit unit,//时间单位
BlockingQueue<Runnable> workQueue,//任务队列,用来储存等待执行任务的队列
ThreadFactory threadFactory,//线程工厂,用来创建线程,一般默认即可
RejectedExecutionHandler handler) //拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务
RejectedExecutionHandler 拒绝策略,当线程池中的工作线程达到了最大数量, 并且阻塞队列也已经满了,那么拒绝策略会决定如何处理新的任务。 ThreadPoolExecutor 提供了四种策略:
-
AbortPolicy(是线程池的默认拒绝策略):
如果使用此拒绝策略,那么将对新的任务抛出
RejectedExecutionException
异常,来拒绝任务。 -
DiscardPolicy:
如果使用此策略,那么会拒绝执行新的任务,但不会抛出异常。
-
DiscardOldestPolicy:
如果使用此策略,那么不会拒绝新的任务, 但会抛弃阻塞队列中等待最久的那个线程。
-
CallerRunsPolicy:
如果使用此策略,不会拒绝新的任务,但会让调用者执行线程(比如Main线程)。 也就是说哪个线程发出的任务,哪个线程执行。
线程池原理总结:
- 在创建了线程池后,等来提交过来的任务请求
- 当调用 execute() 方法添加一个请求任务时,线程池会做如下判断:
- 如果正在运行的线程池数量小于 corePoolSize,那么马上创建新线程执行任务
- 如果正在运行的线程池数量大于或等于 corePoolSize,那么将这个任务放入等待队列
- 如果这个时候等待队列满了且正在运行的线程数量小于 maximumPoolSize ,那么会创建非核心线程来执行任务
- 如果这个时候等待队列满了且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会启动饱和拒绝策略来执行
- 当一个线程执行完成任务时,会从队列中取下一个任务来执行
- 当一个线程无事可做超过一定时间(keepAliveTime)时,线程池会判断:如果当前运行的线程数量大于 corePoolSize,那么这个线程会被停掉
所以线程池的所有任务完成后它最终会收缩到 corePoolSize 的大小
为什么不用 Java 自带的线程池?
Executors 返回的线程池对象的弊端如下:
1、newFixedThreadPool 和 newSingleThreadExecutor
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
}
LinkedBlockingQueue 队列的默认长度为 Integer.MAX_VALUE(21亿) ,可能会堆积大量请求,从而导致 OOM
2、newCachedThreadPool 和 newScheduledThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
}
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());
}
可以看到允许创建的线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM
手写一个线程池?
public class ThreadPoolExecetorDemo {
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(3,
5,
2,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy());
//有10个人来柜台办理业务...
for (int i = 1; i <= 10; i++) {
executorService.execute(() -> {
System.out.println(Thread.currentThread().getName() + "\t处理。。");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executorService.shutdown();
}
}
https://github.com/Seazean/JavaNote
https://github.com/xiaolincoder/CS-Base/blob/main/network/4_ip/ip_base.md
https://blog.csdn.net/xjjj064/article/details/120214975
https://blog.csdn.net/cd546566850/article/details/105353791
https://blog.csdn.net/TZ845195485/article/details/93238857