Java多线程 - 线程池
# 为什么要有线程池?
线程池能够对线程进行统一分配,调优和监控:
- 降低资源消耗(线程无限制地创建,然后使用完毕后销毁)
- 提高响应速度(无须创建线程)
- 提高线程的可管理性
池化技术
池化技术指的是提前准备一些资源,在需要时可以重复使用这些预先准备的资源。
也就是说池化技术有两个优点:
- 提前创建;
- 重复利用。
常见的池化技术的使用有:线程池、内存池、数据库连接池、HttpClient 连接池等
# 如何创建?
# Executors 线程池工具类
# newFixedThreadPool(指定线程数线程池)
newFixedThreadPool(int nThreads)
workQueue.capacity = Integer.MAX_VALUE
:任务队列无限长
# newSingleThreadExecutor(单线程线程池)
newSingleThreadExecutor()
workQueue.capacity = Integer.MAX_VALUE
:任务队列无限长
# newCachedThreadPool(动态扩容线程池)
newCachedThreadPool()
maximumPoolSize = Integer.MAX_VALUE
:最大线程数无限大
以上方法均返回 ExecutorService
接口对象
点击查看
ExecutorService executorService = Executors.newFixedThreadPool(1);
// 只能提交Runnable类型的任务
executorService.execute(() -> {
// 会直接抛出任务执行时的异常
System.out.println("hello world");
});
// submit既能提交Runnable类型任务也能提交Callable类型任务
executorService.submit(() -> {
// 不抛出任务执行时的异常
System.out.println("hello world");
});
// 关闭线程池
executorService.shutdown();
execute 和 submit 的区别与联系
点击查看
execute 和 submit 都属于线程池的方法,execute 只能提交 Runnable 类型的任务,而 submit 既能提交 Runnable 类型任务也能提交 Callable 类型任务。
execute 会直接抛出任务执行时的异常,submit 会吃掉异常,可通过 Future 的 get 方法将任务执行时的异常重新抛出。
execute 所属顶层接口是 Executor,submit 所属顶层接口是 ExecutorService,实现类 ThreadPoolExecutor 重写了 execute 方法,抽象类 AbstractExecutorService 重写了 submit 方法。
笔记
三个工具类方法,在 workQueue.capacity
或是 maximumPoolSize
上使用了 Integer.MAX_VALUE
容易导致 OOM(Out Of Memory),在阿里巴巴开发手册中不推荐使用,而是自己定义。
# ThreadPoolExecutor 自定义线程池
# 参数
int corePoolSize
:常驻核心线程数(常驻柜台数)int maximumPoolSize
:最大线程数(最大柜台数)- CPU 密集型的任务,电脑核心数 + 1(
Runtime.getRuntime().availableProcessors() + 1
) - IO 密集型的任务,电脑核心数 / 阻塞系数(
阻塞时间 / (阻塞时间 + 计算时间)
)
- CPU 密集型的任务,电脑核心数 + 1(
long keepAliveTime
:与下面一个参数配合使用TimeUnit unit
:当空闲的时间达到keepAliveTime
unit
时销毁多余的线程BlockingQueue<Runnable> workQueue
:任务队列,被提交但尚未被执行的任务(候客区)ThreadFactory threadFactory
:表示线程池中的工作线程的线程工厂,用于创建线程。一般默认的即可(Executors.defaultThreadFactory ())RejectedExecutionHandler handler
:拒绝策略,表示当队列满了,并且工作线程大于等于maximumPoolSize
时如何来拒绝请求执行的任务 (runable) 的策略new ThreadPoolExecutor.AbortPolicy()
:(默认)直接抛出 RejectedExecutionException 异常阻止系统正常运行new ThreadPoolExecutor.CallerRunsPolicy()
:调用者运行,一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退调用者,从而降低新任务的流量。(main 线程找线程池处理,处理不了,返回 main 处理)new ThreadPoolExecutor.DiscardPolicy()
:该策略默默地丢弃无法处理的任务,不予任何处理、不抛异常。如果允许任务丢失,这是最好的一种策略new ThreadPoolExecutor.DiscardOldestPolicy()
:将队列中最久的任务丢弃,将当前任务放进来。
# 新任务流程图
工作核心数与队列的关系
- 当任务数量大于核心线程数时,将任务加入阻塞队列
- 当阻塞队列满了,依次扩容核心线程数直至最大线程数
- 当达到最大线程数,再次提交任务,将执行拒绝策略
# 回收空闲线程
# 参考资料
上次更新: 2024/03/11, 22:37:05