本文最后更新于 2025年8月6日 凌晨
线程池
状态设计
RUNNING : 表示线程池处于运行状态,此时线程池能够接收新提交的任务,并且能够处理阻塞队列中的任务
SHUTDOWN : 表示线程池处于关闭状态,此时线程池不能接收新提交的任务,但是不会中断正在执行任务的线程,能够继续执行正在执行的任务,也能够处理阻塞队列中已经保存的任务。如果线程池处于RUNNING状态,那么调用线程池的shutdown()方法会使线程池进入SHUTDOWN状态
STOP :表示线程池处于停止状态,此时线程池不能接收新提交的任务,也不能继续处理阻塞队列中的任务,同时会中断正在执行任务的线程,那么调用线程池的shutdownNow()方法会使线程池进入STOP状态
TIDYING :如果线程池中所有的任务都已经终止,有效线程数为0,线程池就会进入TIDYING状态。换句话说,线程池中已经没有正在执行的任务,并且线程池中的阻塞队列为空,同时线程池中的工作线程数量为0,线程池就会进入TIDYING状态
TERMINATED : 如果线程池处于TIDYING状态,此时调用线程池的terminated()方法,线程池就会进入TERMINATED状态
状态转移过程
- 当线程池处于RUNNING状态时,显式调用线程池的shutdown 方法,或者隐式调用finalize()方法,线程池会由RUNNING状态转换为SHUTDOWN状态
- 当线程池处于RUNNING状态时,显式调用线程池的shutdownNow()方法,线程池会由RUNNING状态转换为STOP状态
- 当线程池处于SHUTDONW状态时,显式调用线程池的shutdownNow()方法,线程池会由SHUTDOWN状态转换为STOP状态
- 当线程池处于SHUTDOWN状态时,如果线程池中无工作线程,并且阻塞队列为空,则线程池会由SHUTDOWN状态转换为TIDYING状态
- 当线程池处于STOP状态时,如果线程池中无工作线程,则线程池会由STOP状态转换为TIDYING状态
- 当线程池处于TIDYING状态时,调用线程池的terminated()方法,线程池会由TIDYING状态变为TERMINATED状态
创建线程池
Executors
executors 提供了多种线程池创建方式
- Executors.newCachedThreadPool 方法
创建一个可缓存的线程池,如果线程池中的线程超过了运行任务的需要,则可以灵活地回收线程,再向线程池中提交任务,则会新建线程来执行任务
- Executors.newFixedThreadPool方法
创建一个线程数量固定的线程池,能够有效地控制线程池的最大并发数,当向线程池中提交任务时,如果线程池中有空闲线程,则执行任务。如果线程池中无空闲线程,则将任务放入阻塞队列中,待线程池中出现空闲线程,再执行阻塞队列中的任务。
- Executors.newScheduledThreadPool 方法
创建一个计划线程池
- Executors.newSingleThreadExecutor 方法
创建一个只有一个线程的线程池,能够保证提交到线程池中的所有任务按照先进先出的顺序,或者按照某个优先级的顺序来执行,当向线程池中提交任务时,如果线程池中无空闲线程,则会将任务保存在阻塞队列中。
- Executors.newSingleThreadScheduledExecutor方法
创建一个只有一个线程的线程池,并进行周期性执行任务
ThreadPoolExecutors
Executors 在ThreadPoolExecutors 的基础上进行扩展
ThreadPoolExecutors 的基础构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public ThreadPoolExecutor(int corePoolSize , int maximumPoolSize, long keepAliveTime , BlockingQueue<Runnale> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler){ if (corePoolSize > 0 || maximumPoolSize<= 0 || maximumPoolSize< corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.acc = System.getSecurityManager() == null ? null : AccessController.getContext(); this.corePoolSize = corePoolSize ; this.maximumPoolSize = maximumPoolSize ; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory ; this.handler = handler ; } }
|
corePoolSize : 表示线程池中的核心线程数
maximumPoolSize : 表示线程池中的最大线程数
keepAliveTime : 在表示线程池中的线程空闲时,能够保持的最长时间
unit : 表示keepAliveTime的单位时间
workQueue : 表示线程中的阻塞队列
threadFactory : 表示用来创建线程的线程工厂
handler : 表示线程池拒绝处理任务的时的策略
- 当线程池中运行的线程数小于corePoolSize时,如果向 线程池中提交任务,那么即使线程池中存在空闲队列,也会直接创建新线程来执行任务
- 如果线程池中运行的线程数大于corePoolSize,并且小于maximumPoolSize,那么只有当workQueue队列已满时,才会创建新的线程来执行新提交的任务
- 在调用ThreadPoolExecutor类的构造方法时,如果传递的corePoolSize和maximumPoolSize参数相同,那么创建的线程池大小是固定的,此时如果向线程池中提交任务,并且workQueue队列未满,就会将新提交的任务保存到workQueue队列中,等待空闲的线程,从workQueue队列中获取任务并执行
- 如果线程池中运行的线程数大于maximumPoolSize,并且此时workQueue队列已满,则会触发指定的拒绝策略来拒绝任务的执行
拒绝策略
如果线程池中的workQueue阻塞队列已满,同时线程池中的线程数已经达到maximumPoolSize,且没有空闲线程,此时继续有任务提交到线程池,就需要采取某种策略来拒绝任务的执行
在ThreadPoolExecutor类的executor()方法中,会在适当时机调用reject(command)方法来执行拒绝策略
1 2 3
| final void reject(Runnable command){ handler.rejectedExecution(command,this); }
|
在reject(command)方法中调用了handler的rejectedExecution()方法,这里,在ThreadPoolExecutor类中声明了handler变量,
1
| private volatile RejctedExecutionHandler handler;
|
拒绝策略执行
1 2 3
| public interface RejectedExecutionHandler{ void rejectedExecution(Runnable r, ThreadPoolExecutor executor); }
|
RejctedExecutionHandler 有四个默认实现 分别代表
- AbortPolicy 当线程池无法处理新任务时,直接抛出RejectedExecutionException异常
- CallerRunsPolicy 当线程池无法处理新任务时,让提交任务的线程(调用者线程)直接执行该任务
- DiscardOldestPolicy 丢弃队列中最老的(等待时间最长的)任务
- DiscardPolicy 直接丢弃无法处理的新任务
根据最初的传入参数来选择对应的策略
线程池关闭
使用shutdown 或者shutdownNow关闭线程池
线程池线程数设置
I/O 密集 : 使用I/O阻塞时间 所以是 I/O阻塞时间/CPU计算耗时
CPU 密集 : 一般与CPU核心数有关,或者+1 防止所有线程都在阻塞状态CPU闲置
ThreadLocal
ThreadLocal 是 Java 提供的一种线程隔离机制,它为每个使用该变量的线程提供独立的变量副本,每个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class ThreadLocalTest{ private static final ThreadLocal<String> THREAD_LOCAL = new ThreadLocal<String>(); public static void main(String[] args){ Thread threadA = new Thread( ()-> { THREAD_LOCAL.set("ThreadA: " + Thread.currentThread().getName()); System.out.println(Thread.currentThread().getName()+"本地变量中的值为"+ THREAD_LOCAL.get()); },"Thread-A");
Thread threadB = new Thread( ()-> { THREAD_LOCAL.set("ThreadB: " + Thread.currentThread().getName()); System.out.println(Thread.currentThread().getName()+"本地变量中的值为"+ THREAD_LOCAL.get()); },"Thread-B"); threadA.start(); threadB.start(); } }
|
threadlocal的核心原理是
在编译时,给出了定义内容
1
| private static final ThreadLocal<String> THREAD_LOCAL = new ThreadLocal<String>();
|
当运行时线程结构中定义了存储线程局部变量的结构
1 2 3 4
|
ThreadLocal.ThreadLocalMap threadLocals = null; ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
|
当使用set方法时,ThreadLocal 先获取当前线程,再通过getMap获取到本地的ThreadLocalMap 之后向Map中设置局部变量。
1 2 3 4 5 6 7 8
| public void set(T value){ Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if(map!=null) map.set(this,value); else createMap(t,value); }
|
1 2 3 4
| ThreadLocalMap getMap(Thread t){ retun t.threadLocals }
|
get方法, 与set一样 获取thread 的本地变量容器结构,从中获取对象,并转换为目标类型对象
1 2 3 4 5 6 7 8 9 10 11 12
| public T get(){ Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if(map != null){ ThreadLocalMap.Entry e = map.getEntry(this); if(e != null){ @SuppressWarnings("unchecked") T result = (T)e.value; return result ; } } }
|
setInitialvalue()方法
1 2 3 4 5 6 7 8 9 10
| private T setInitialvalue(){ T value = initialvalue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if(map!=null) map.set(this,value); else createMap(t,value); return value ; }
|
1 2 3
| protected T initialvalue(){ return null ; }
|
remove 方法
1 2 3 4 5 6
| public void remove(){ ThreadLocalMap m = getMap(Thread.currentThread()); if(m!=null){ m.remove(this); } }
|
InheritableThreadLocal
InheritableThreadLocal 会获得在主线程中存储的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class InheritableThreadLocalTest{ private static final ThreadLocal<String> THREAD_LOCAL = new InheritableThreadLocal<String>(); public static void main(String[] args){ THREAD_LOCAL.set("codfish"); }
public static void main(String[] args){ new Thread(()->{ System.out.println("子线程中的本地变量值为"+THREAD_LOCAL.get()); }).start(); System.out.println("在主线程中获取到的本地变量值为"+THREAD_LOCAL.get()); } }
|
InheritableThreadLocal 的定义
1 2 3 4 5 6 7 8 9 10 11
| public class InheritableThreadLocal<T> extends ThreadLocal<T>{ protected T childvalue(T parentvalue){ return parentvalue ; } ThreadLocalMap getMap(Thread t){ return t.inheritableThreadLocals; } void createMap(Thread t , T firstvalue){ t.inheritableThreadLocals = new ThreadLocalMap(this,firstvalue); } }
|
Thread的构造方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public Thread(){ init(null,null, "Thread-" + nextThreadNum(),0); } public Thread(Runnable target){ init(null,target,"Thread-" + nextThreadNum(),0); } Thread(Runnable target, AccessControlContext acc){ init(null,target,"Thread-" + nextThreadNum(),0,acc,false); } public Thread(ThreadGroup group , Runnable target){ init(group,target, "Thread-" + nextThreadNum(),0); } public Thread(String name){ init(null,null,name,0); } public Thread(ThreadGroup group , String name){ init(group,null,name,0); } public Thread(ThreadGroup group , Runnable target, String name ){ init(group,target,name,0); } public Thread(ThreadGroup group , Runnable target, String name , long stackSize){ init(group,target,name,stackSize); }
|
而初始化方法
1 2 3 4 5 6
| private void init(ThreadGroup g, Runnable target , String name , long stackSize , AccessContext acc , boolean inheritThreadLocals){ if(inheritThreadLocals && parent.inheritableThreadLocals !=null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); this.stackSize = stackSize; tid = nextThreadID(); }
|
当父线程中的inheritableThreadLocals成员变量为true 是存在的,且不为null,则进行子线程中的inheritable 创建过程
1 2 3
| static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap){ return new ThreadLocalMap(parentMap); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| private ThreadLocalMap(ThreadLocalMap parentMap){ Entry[] parentTable = parentMap.table ; int len = parentTable.length ; setThreadhold(len); table = new Entry[len]; for (int j=0 ; j < len ; j++){ if(e != null){ @SuppressWarnings("unchecked") ThreadLocal<Object> key = (ThreadLocal<Object>) e.get(); if (key != null){ @SuppressWarnings("unchecked") ThreadLocal<Object> key = (ThreadLocal<Object>) e.get(); if(key != null){ Object value = key.childvalue(e.value); Entry c = new Entry(key, value); int h = key.threadLocalHashCode & (len - 1); while (table[h] != null){ h = nextIndex(h,len); } table[h] = c; size ++ ; } } } }
|
线程池与ThreadLocal技术文档
线程池
状态设计
RUNNING :表示线程池处于运行状态,此时线程池能够接收新提交的任务,并且能够处理阻塞队列中的任务。
SHUTDOWN :表示线程池处于关闭状态,此时线程池不能接收新提交的任务,但是不会中断正在执行任务的线程,能够继续执行正在执行的任务,也能够处理阻塞队列中已经保存的任务。如果线程池处于RUNNING状态,那么调用线程池的shutdown()方法会使线程池进入SHUTDOWN状态。
STOP :表示线程池处于停止状态,此时线程池不能接收新提交的任务,也不能继续处理阻塞队列中的任务,同时会中断正在执行任务的线程。调用线程池的shutdownNow()方法会使线程池进入STOP状态。
TIDYING :如果线程池中所有的任务都已经终止,有效线程数为0,线程池就会进入TIDYING状态。换句话说,线程池中已经没有正在执行的任务,并且线程池中的阻塞队列为空,同时线程池中的工作线程数量为0,线程池就会进入TIDYING状态。
TERMINATED :如果线程池处于TIDYING状态,此时调用线程池的terminated()方法,线程池就会进入TERMINATED状态。
状态转移过程
- 当线程池处于RUNNING状态时,显式调用线程池的shutdown()方法,或者隐式调用finalize()方法,线程池会由RUNNING状态转换为SHUTDOWN状态
- 当线程池处于RUNNING状态时,显式调用线程池的shutdownNow()方法,线程池会由RUNNING状态转换为STOP状态
- 当线程池处于SHUTDOWN状态时,显式调用线程池的shutdownNow()方法,线程池会由SHUTDOWN状态转换为STOP状态
- 当线程池处于SHUTDOWN状态时,如果线程池中无工作线程,并且阻塞队列为空,则线程池会由SHUTDOWN状态转换为TIDYING状态
- 当线程池处于STOP状态时,如果线程池中无工作线程,则线程池会由STOP状态转换为TIDYING状态
- 当线程池处于TIDYING状态时,调用线程池的terminated()方法,线程池会由TIDYING状态变为TERMINATED状态
创建线程池
Executors工厂类
Executors提供了多种线程池创建方式:
Executors.newCachedThreadPool方法 :创建一个可缓存的线程池,如果线程池中的线程超过了运行任务的需要,则可以灵活地回收线程,再向线程池中提交任务,则会新建线程来执行任务。
Executors.newFixedThreadPool方法 :创建一个线程数量固定的线程池,能够有效地控制线程池的最大并发数,当向线程池中提交任务时,如果线程池中有空闲线程,则执行任务。如果线程池中无空闲线程,则将任务放入阻塞队列中,待线程池中出现空闲线程,再执行阻塞队列中的任务。
Executors.newScheduledThreadPool方法 :创建一个计划线程池,支持周期性任务执行。
Executors.newSingleThreadExecutor方法 :创建一个只有一个线程的线程池,能够保证提交到线程池中的所有任务按照先进先出的顺序,或者按照某个优先级的顺序来执行,当向线程池中提交任务时,如果线程池中无空闲线程,则会将任务保存在阻塞队列中。
Executors.newSingleThreadScheduledExecutor方法 :创建一个只有一个线程的线程池,并进行周期性执行任务。
ThreadPoolExecutor详解
Executors在ThreadPoolExecutor的基础上进行扩展
ThreadPoolExecutor的基础构造器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.acc = System.getSecurityManager() == null ? null : AccessController.getContext(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
|
参数说明 :
corePoolSize:表示线程池中的核心线程数
maximumPoolSize:表示线程池中的最大线程数
keepAliveTime:表示线程池中的线程空闲时,能够保持的最长时间
unit:表示keepAliveTime的单位时间
workQueue:表示线程池中的阻塞队列
threadFactory:表示用来创建线程的线程工厂
handler:表示线程池拒绝处理任务时的策略
线程池工作原理详解
任务提交和执行的完整流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public void execute(Runnable command) { int c = ctl.get();
if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; c = ctl.get(); }
if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); if (!isRunning(recheck) && remove(command)) reject(command); else if (workerCountOf(recheck) == 0) addWorker(null, false); }
else if (!addWorker(command, false)) reject(command); }
|
线程复用机制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| final void runWorker(Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; w.firstTask = null;
try { while (task != null || (task = getTask()) != null) { w.lock(); try { beforeExecute(wt, task); task.run(); afterExecute(task, null); } finally { task = null; w.completedTasks++; w.unlock(); } } } finally { processWorkerExit(w, completedAbruptly); } }
|
执行规则详解
- 当线程池中运行的线程数小于corePoolSize时,如果向线程池中提交任务,那么即使线程池中存在空闲线程,也会直接创建新线程来执行任务
- 如果线程池中运行的线程数大于corePoolSize,并且小于maximumPoolSize,那么只有当workQueue队列已满时,才会创建新的线程来执行新提交的任务
- 在调用ThreadPoolExecutor类的构造方法时,如果传递的corePoolSize和maximumPoolSize参数相同,那么创建的线程池大小是固定的,此时如果向线程池中提交任务,并且workQueue队列未满,就会将新提交的任务保存到workQueue队列中,等待空闲的线程,从workQueue队列中获取任务并执行
- 如果线程池中运行的线程数大于maximumPoolSize,并且此时workQueue队列已满,则会触发指定的拒绝策略来拒绝任务的执行
拒绝策略
如果线程池中的workQueue阻塞队列已满,同时线程池中的线程数已经达到maximumPoolSize,且没有空闲线程,此时继续有任务提交到线程池,就需要采取某种策略来拒绝任务的执行。
在ThreadPoolExecutor类的execute()方法中,会在适当时机调用reject(command)方法来执行拒绝策略:
1 2 3 4
| final void reject(Runnable command) { handler.rejectedExecution(command, this); }
|
在reject(command)方法中调用了handler的rejectedExecution()方法,这里,在ThreadPoolExecutor类中声明了handler变量:
1 2
| private volatile RejectedExecutionHandler handler;
|
拒绝策略接口定义:
1 2 3 4
| public interface RejectedExecutionHandler { void rejectedExecution(Runnable r, ThreadPoolExecutor executor); }
|
RejectedExecutionHandler有四个默认实现,分别代表:
- AbortPolicy :当线程池无法处理新任务时,直接抛出RejectedExecutionException异常
- CallerRunsPolicy :当线程池无法处理新任务时,让提交任务的线程(调用者线程)直接执行该任务
- DiscardOldestPolicy :丢弃队列中最老的(等待时间最长的)任务
- DiscardPolicy :直接丢弃无法处理的新任务
线程池关闭
使用shutdown()或者shutdownNow()关闭线程池:
shutdown():平滑关闭,不再接受新任务,但会执行完已提交的任务
shutdownNow():立即关闭,尝试停止所有正在执行的任务,并返回等待执行的任务列表
线程池线程数设置建议
I/O密集型任务 :线程数 = CPU核心数 × (1 + I/O阻塞时间/CPU计算时间)
CPU密集型任务 :线程数 = CPU核心数 + 1(+1是为了防止所有线程都在阻塞状态时CPU闲置)
ThreadLocal
ThreadLocal是Java提供的一种线程隔离机制,它为每个使用该变量的线程提供独立的变量副本,每个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。
基本使用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class ThreadLocalTest { private static final ThreadLocal<String> THREAD_LOCAL = new ThreadLocal<String>();
public static void main(String[] args) { Thread threadA = new Thread(() -> { THREAD_LOCAL.set("ThreadA: " + Thread.currentThread().getName()); System.out.println(Thread.currentThread().getName() + "本地变量中的值为" + THREAD_LOCAL.get()); }, "Thread-A");
Thread threadB = new Thread(() -> { THREAD_LOCAL.set("ThreadB: " + Thread.currentThread().getName()); System.out.println(Thread.currentThread().getName() + "本地变量中的值为" + THREAD_LOCAL.get()); }, "Thread-B");
threadA.start(); threadB.start(); } }
|
ThreadLocal核心原理
ThreadLocal的核心原理是在编译时给出了定义内容:
1 2
| private static final ThreadLocal<String> THREAD_LOCAL = new ThreadLocal<String>();
|
当运行时,线程结构中定义了存储线程局部变量的结构:
1 2 3 4
| ThreadLocal.ThreadLocalMap threadLocals = null; ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
|
set方法实现
ThreadLocal先获取当前线程,再通过getMap获取到本地的ThreadLocalMap,之后向Map中设置局部变量:
1 2 3 4 5 6 7 8 9 10 11 12 13
| public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
|
get方法实现
与set一样,获取thread的本地变量容器结构,从中获取对象,并转换为目标类型对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
|
setInitialValue方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; }
protected T initialValue() { return null; }
|
remove方法
1 2 3 4 5 6 7
| public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) { m.remove(this); } }
|
ThreadLocal内存泄漏风险及防范
泄漏原理
ThreadLocal的内存泄漏主要源于其内部结构:
1 2 3 4 5 6 7 8 9
| static class Entry extends WeakReference<ThreadLocal<?>> { Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } }
|
泄漏场景示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class MemoryLeakExample { public void problematicUsage() { ThreadLocal<List<String>> local = new ThreadLocal<>(); local.set(new ArrayList<>(Collections.nCopies(1000000, "data")));
local = null;
}
public void correctUsage() { ThreadLocal<List<String>> local = new ThreadLocal<>(); try { local.set(new ArrayList<>()); } finally { local.remove(); } } }
|
在线程池中的风险
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public class ThreadPoolLeakExample { private static final ThreadLocal<ExpensiveObject> CACHE = new ThreadLocal<ExpensiveObject>() { @Override protected ExpensiveObject initialValue() { return new ExpensiveObject(); } };
public void processInThreadPool() { ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) { executor.submit(() -> { try { ExpensiveObject obj = CACHE.get(); } finally { CACHE.remove(); } }); }
executor.shutdown(); } }
|
关键提醒 :在线程池环境中,由于线程复用,ThreadLocal数据会在多个任务间保持,必须在每个任务结束后主动调用remove()方法清理。
InheritableThreadLocal
InheritableThreadLocal会获得在主线程中存储的值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class InheritableThreadLocalTest { private static final ThreadLocal<String> THREAD_LOCAL = new InheritableThreadLocal<String>();
public static void main(String[] args) { THREAD_LOCAL.set("codfish");
new Thread(() -> { System.out.println("子线程中的本地变量值为" + THREAD_LOCAL.get()); }).start();
System.out.println("在主线程中获取到的本地变量值为" + THREAD_LOCAL.get()); } }
|
InheritableThreadLocal的定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class InheritableThreadLocal<T> extends ThreadLocal<T> { protected T childValue(T parentValue) { return parentValue; }
ThreadLocalMap getMap(Thread t) { return t.inheritableThreadLocals; }
void createMap(Thread t, T firstValue) { t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue); } }
|
继承机制原理
Thread的构造方法最终都会调用init方法:
1 2 3 4 5 6 7 8 9 10 11 12
| private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) {
if (inheritThreadLocals && parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
this.stackSize = stackSize; tid = nextThreadID(); }
|
创建继承映射的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) { return new ThreadLocalMap(parentMap); }
private ThreadLocalMap(ThreadLocalMap parentMap) { Entry[] parentTable = parentMap.table; int len = parentTable.length; setThreshold(len); table = new Entry[len];
for (int j = 0; j < len; j++) { Entry e = parentTable[j]; if (e != null) { @SuppressWarnings("unchecked") ThreadLocal<Object> key = (ThreadLocal<Object>) e.get(); if (key != null) { Object value = key.childValue(e.value); Entry c = new Entry(key, value); int h = key.threadLocalHashCode & (len - 1); while (table[h] != null) h = nextIndex(h, len); table[h] = c; size++; } } } }
|
InheritableThreadLocal的局限性
1. 线程池复用场景的问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class InheritableThreadLocalProblem { private static final InheritableThreadLocal<String> CONTEXT = new InheritableThreadLocal<>();
public static void main(String[] args) throws InterruptedException { ExecutorService executor = Executors.newFixedThreadPool(2);
CONTEXT.set("Request-1"); executor.submit(() -> { System.out.println("Task1: " + CONTEXT.get()); });
Thread.sleep(100);
CONTEXT.set("Request-2"); executor.submit(() -> { System.out.println("Task2: " + CONTEXT.get()); });
executor.shutdown(); } }
|
2. 继承时机的限制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class InheritanceTiming { private static final InheritableThreadLocal<String> INHERITABLE = new InheritableThreadLocal<>();
public void demonstrateInheritanceTiming() { Thread thread1 = new Thread(() -> { System.out.println("Thread1: " + INHERITABLE.get()); });
INHERITABLE.set("ParentValue"); thread1.start();
INHERITABLE.set("ParentValue2"); Thread thread2 = new Thread(() -> { System.out.println("Thread2: " + INHERITABLE.get()); }); thread2.start(); } }
|
3. 深拷贝问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class InheritableDeepCopyIssue { private static final InheritableThreadLocal<List<String>> LIST_CONTEXT = new InheritableThreadLocal<List<String>>() { @Override protected List<String> childValue(List<String> parentValue) {
return parentValue == null ? null : new ArrayList<>(parentValue); } };
public void demonstrateShallowCopy() throws InterruptedException { List<String> list = new ArrayList<>(); list.add("item1"); LIST_CONTEXT.set(list);
Thread childThread = new Thread(() -> { List<String> childList = LIST_CONTEXT.get(); childList.add("item2"); System.out.println("Child: " + childList); });
childThread.start(); childThread.join();
System.out.println("Parent: " + LIST_CONTEXT.get()); } }
|
最佳实践建议
ThreadLocal使用建议
- 总是在finally块中调用remove() ,特别是在线程池环境中
- 避免在ThreadLocal中存储大对象 ,减少内存泄漏风险
- 使用static final修饰ThreadLocal变量 ,避免创建多个实例
- 考虑使用try-with-resources模式封装ThreadLocal操作
线程池配置建议
- 根据任务类型合理设置线程数 :I/O密集型和CPU密集型采用不同策略
- 选择合适的阻塞队列 :有界队列防止内存溢出
- 自定义ThreadFactory :设置有意义的线程名称,便于问题排查
- 监控线程池状态 :定期检查活跃线程数、队列大小等指标
- 优雅关闭线程池 :先调用shutdown(),等待一定时间后再调用shutdownNow()
InheritableThreadLocal使用建议
- 避免在线程池环境中使用 ,考虑使用其他上下文传递方案
- 重写childValue方法实现深拷贝,避免父子线程间的数据污染
- 注意继承时机 ,确保在创建子线程前设置父线程的值
- 考虑使用TransmittableThreadLocal等第三方库来解决线程池场景下的上下文传递问题