手撕Java线程池

简介: 原理和源码解析

一、概述

配合源码熟悉线程池结构。
1、Executor
Executor是线程池架构的核心,是Java异步任务的执行者接口,目标是执行目标任务。Executor作为执行者角色,目的是提供一种将“任务提交者”与“任务执行者”分离的机制。它只有一个函数式方法:
image.png
2、 ExecutorService
ExecutorService继承于Executor。它对外提供异步任务的接收服务。ExecutorService提供了“接受异步任务并转交给执行者”的方法,比如submit、invoke方法等。具体如下:
image.png
3、ExecutorService抽象类
image.png
比如AbstractEventExecutor,它实现了ExecutorService接口。AbstractExecutorService存在的目的是为ExecutorService中的接口提供默认实现。(模板模式)
image.png
4、实现类
image.png
比如ThreadPoolExecutor,继承于AbstractExecutorService。它是核心实现类,它可以预先提供指定数量的可重用线程,可以对线程进行管理和监控。
5、ScheduledExecutorService
继承于ExecutorService。是一个完成延时和周期性任务的接口。
image.png
6、Executors
是一个静态工厂类,内置的静态工厂方法可以理解为快捷创建线程池的方法。
image.png
所以我们就清晰的了解了线程池架构的完整结构
image.png

二、如何创建线程池

1、newFixedThreadPool 创建固定大小的线程池

使用Executors最常用的莫过于是使用:Executors.newFixedThreadPool(int)这个方法,因为它既可以限制数量,而且线程用完后不会一直被cache住;那么就通过它来看看源码,回过头来再看其他构造方法的区别:

        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue());
}

我们看一看ThreadPoolExecutor的源码是怎么样的
image.png
参数说明:
corePoolSize:核心线程数。
maximumPoolSize:最大线程数。
keepAliveTime:线程存活时间。当线程数大于core数,那么超过该时间的线程将会被终结。
unit:keepAliveTime的单位。java.util.concurrent.TimeUnit类存在静态静态属性: NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS
workQueue:Runnable的阻塞队列。若线程池已经被占满,则该队列用于存放无法再放入线程池中的Runnable。
其中handler表示线程池对拒绝任务的处理策略。
若线程池中的线程数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
若线程池中的线程数量等于 corePoolSize且缓冲队列 workQueue未满,则任务被放入缓冲队列。
若线程池中线程的数量大于corePoolSize且缓冲队列workQueue满,且线程池中的数量小于maximumPoolSize,则建新的线程来处理被添加的任务。
若线程池中线程的数量大于corePoolSize且缓冲队列workQueue满,且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。
2、newSingleThreadExecutor
image.png
这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。
返回单线程的Executor,将多个任务交给此Exector时,这个线程处理完一个任务后接着处理下一个任务,若该线程出现异常,将会有一个新的线程来替代。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
3、newCachedThreadPool
image.png
在执行任务时,如果池内所有线程忙,则会添加新线程来处理。不会限制线程的大小,完全依赖于操作系统能够创建的最大线程大小。如果存量线程超过了处理任务数量,就会回收线程。
4、newScheduledThreadPool
因为可以周期性执行任务,所以不shutdown。
适用场景:周期性执行任务的场景。
image.png

目录
相关文章
|
9天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
|
3月前
|
Java 调度 数据库
Java并发编程:深入理解线程池
在Java并发编程的海洋中,线程池是一艘强大的船,它不仅提高了性能,还简化了代码结构。本文将带你潜入线程池的深海,探索其核心组件、工作原理及如何高效利用线程池来优化你的并发应用。
|
3月前
|
存储 监控 Java
Java多线程优化:提高线程池性能的技巧与实践
Java多线程优化:提高线程池性能的技巧与实践
115 1
|
3月前
|
安全 Java 数据库
一天十道Java面试题----第四天(线程池复用的原理------>spring事务的实现方式原理以及隔离级别)
这篇文章是关于Java面试题的笔记,涵盖了线程池复用原理、Spring框架基础、AOP和IOC概念、Bean生命周期和作用域、单例Bean的线程安全性、Spring中使用的设计模式、以及Spring事务的实现方式和隔离级别等知识点。
|
3月前
|
存储 监控 安全
一天十道Java面试题----第三天(对线程安全的理解------>线程池中阻塞队列的作用)
这篇文章是Java面试第三天的笔记,讨论了线程安全、Thread与Runnable的区别、守护线程、ThreadLocal原理及内存泄漏问题、并发并行串行的概念、并发三大特性、线程池的使用原因和解释、线程池处理流程,以及线程池中阻塞队列的作用和设计考虑。
|
21天前
|
监控 安全 Java
在 Java 中使用线程池监控以及动态调整线程池时需要注意什么?
【10月更文挑战第22天】在进行线程池的监控和动态调整时,要综合考虑多方面的因素,谨慎操作,以确保线程池能够高效、稳定地运行,满足业务的需求。
100 38
|
21天前
|
Prometheus 监控 Cloud Native
JAVA线程池监控以及动态调整线程池
【10月更文挑战第22天】在 Java 中,线程池的监控和动态调整是非常重要的,它可以帮助我们更好地管理系统资源,提高应用的性能和稳定性。
58 4
|
21天前
|
Prometheus 监控 Cloud Native
在 Java 中,如何使用线程池监控以及动态调整线程池?
【10月更文挑战第22天】线程池的监控和动态调整是一项重要的任务,需要我们结合具体的应用场景和需求,选择合适的方法和策略,以确保线程池始终处于最优状态,提高系统的性能和稳定性。
90 2
|
24天前
|
缓存 监控 Java
java中线程池的使用
java中线程池的使用
|
2月前
|
Java 调度 开发者
Java并发编程:深入理解线程池
在Java的世界中,线程池是提升应用性能、实现高效并发处理的关键工具。本文将深入浅出地介绍线程池的核心概念、工作原理以及如何在实际应用中有效利用线程池来优化资源管理和任务调度。通过本文的学习,读者能够掌握线程池的基本使用技巧,并理解其背后的设计哲学。