🗄️ThreadLocal
作用
使得每个线程都有自己专属的本地变量,避免了多个线程竞争同一个资源。
原理
每一个线程都有一个ThreadLocalMap
,最终的变量是存储在当前线程的ThreadLocalMap
中,而不是ThreadLocal
中。
键为ThreadLocal实例,值为Object。(ThreadLocalMap是ThreadLocal的静态内部类)
内存泄漏问题
- ThreadLocal实例不再被强引用,故被垃圾回收器回收,key变为null
- 线程持续存活,导致ThreadLocalMap长期存在,但entry却无法被垃圾回收,造成内存泄漏。
避免内存泄漏:使用完ThreadLocal后,务必调用**remove()**方法,可以显示地移除对应的entry。
跨线程传递ThreadLocal的值
- InheritableThreadLocal:使用InheritableThreadLocal时,会在创建子线程时,令子线程继承父线程中的ThreadLocal值。但不支持线程池场景下的ThreadLocal值传递。
- TransmittableThreadLocal:阿里巴巴开源的工具类,支持线程池场景下的ThreadLocal值传递。
🔗线程池
定义
管理一系列线程的资源池。当有任务要处理时,直接从线程池中获取线程来处理,处理完之后线程并不会立即被销毁,而是放回线程池中,等待下一个任务获取。
优点
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
创建
-
通过ThreadPoolExecutor构造函数创建
-
通过Executors框架的工具类创建
Executors.newCachedThreadPool():没有上限的线程池
Executors.newFixedThreadPool(int):有上限的线程池
常见的参数
- 核心线程数
- 最大线程数 = 核心线程数+临时线程
- 空闲线程最大存活时间
- 任务队列
- 线程工厂
- 拒绝策略
拒绝策略
大小决定
为什么要设置大小?
- 线程池数量设置太小,如果同一时间有大量请求需要处理,可能导致大量请求堆积在任务队列中进而OOM(内存溢出)或任务丢失。
- 线程池数量设置太大,大量线程同时争取CPU,导致频繁的上下文切换,减低执行效率。
公式:
- CPU密集型任务(N+1):加一是因为当任务发生中断时,CPU处于空闲状态,可以充分利用CPU的空闲时间。
- I/O密集型任务(2N):线程在处理I/O时,CPU处于空闲状态,这时可以将CPU交给其他线程。
最佳线程数 = N*(1+WT/ST)
N:CPU核心数
WT:线程等待时间
ST:线程计算时间
线程获取流程
获取核心线程,没有则放入等待队列中,等待队列满了的话,就创建临时线程。如果已经达到了最大线程数,就采用拒绝策略。
线程池预热
prestartCoreThread()
:启动一个线程,等待任务,如果已达到核心线程数,这个方法返回 false,否则返回 true;prestartAllCoreThreads()
:启动所有的核心线程,并返回启动成功的核心线程数。
达到核心线程数,这个方法返回 false,否则返回 true;
prestartAllCoreThreads()
:启动所有的核心线程,并返回启动成功的核心线程数。