线程相关

本文最后更新于 2025年8月6日 凌晨

线程池

状态设计

RUNNING : 表示线程池处于运行状态,此时线程池能够接收新提交的任务,并且能够处理阻塞队列中的任务

SHUTDOWN : 表示线程池处于关闭状态,此时线程池不能接收新提交的任务,但是不会中断正在执行任务的线程,能够继续执行正在执行的任务,也能够处理阻塞队列中已经保存的任务。如果线程池处于RUNNING状态,那么调用线程池的shutdown()方法会使线程池进入SHUTDOWN状态

STOP :表示线程池处于停止状态,此时线程池不能接收新提交的任务,也不能继续处理阻塞队列中的任务,同时会中断正在执行任务的线程,那么调用线程池的shutdownNow()方法会使线程池进入STOP状态

TIDYING :如果线程池中所有的任务都已经终止,有效线程数为0,线程池就会进入TIDYING状态。换句话说,线程池中已经没有正在执行的任务,并且线程池中的阻塞队列为空,同时线程池中的工作线程数量为0,线程池就会进入TIDYING状态

TERMINATED : 如果线程池处于TIDYING状态,此时调用线程池的terminated()方法,线程池就会进入TERMINATED状态

状态转移过程

  1. 当线程池处于RUNNING状态时,显式调用线程池的shutdown 方法,或者隐式调用finalize()方法,线程池会由RUNNING状态转换为SHUTDOWN状态
  2. 当线程池处于RUNNING状态时,显式调用线程池的shutdownNow()方法,线程池会由RUNNING状态转换为STOP状态
  3. 当线程池处于SHUTDONW状态时,显式调用线程池的shutdownNow()方法,线程池会由SHUTDOWN状态转换为STOP状态
  4. 当线程池处于SHUTDOWN状态时,如果线程池中无工作线程,并且阻塞队列为空,则线程池会由SHUTDOWN状态转换为TIDYING状态
  5. 当线程池处于STOP状态时,如果线程池中无工作线程,则线程池会由STOP状态转换为TIDYING状态
  6. 当线程池处于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 : 表示线程池拒绝处理任务的时的策略

  1. 当线程池中运行的线程数小于corePoolSize时,如果向 线程池中提交任务,那么即使线程池中存在空闲队列,也会直接创建新线程来执行任务
  2. 如果线程池中运行的线程数大于corePoolSize,并且小于maximumPoolSize,那么只有当workQueue队列已满时,才会创建新的线程来执行新提交的任务
  3. 在调用ThreadPoolExecutor类的构造方法时,如果传递的corePoolSize和maximumPoolSize参数相同,那么创建的线程池大小是固定的,此时如果向线程池中提交任务,并且workQueue队列未满,就会将新提交的任务保存到workQueue队列中,等待空闲的线程,从workQueue队列中获取任务并执行
  4. 如果线程池中运行的线程数大于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
// getMap实现
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状态。

状态转移过程

  1. 当线程池处于RUNNING状态时,显式调用线程池的shutdown()方法,或者隐式调用finalize()方法,线程池会由RUNNING状态转换为SHUTDOWN状态
  2. 当线程池处于RUNNING状态时,显式调用线程池的shutdownNow()方法,线程池会由RUNNING状态转换为STOP状态
  3. 当线程池处于SHUTDOWN状态时,显式调用线程池的shutdownNow()方法,线程池会由SHUTDOWN状态转换为STOP状态
  4. 当线程池处于SHUTDOWN状态时,如果线程池中无工作线程,并且阻塞队列为空,则线程池会由SHUTDOWN状态转换为TIDYING状态
  5. 当线程池处于STOP状态时,如果线程池中无工作线程,则线程池会由STOP状态转换为TIDYING状态
  6. 当线程池处于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
// ThreadPoolExecutor.execute() 方法的核心逻辑
public void execute(Runnable command) {
int c = ctl.get();

// 步骤1:如果当前线程数 < corePoolSize,直接创建核心线程
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}

// 步骤2:核心线程满了,尝试加入队列
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);
}

// 步骤3:队列满了,尝试创建非核心线程
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
// Worker线程的运行逻辑
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);
}
}

执行规则详解

  1. 当线程池中运行的线程数小于corePoolSize时,如果向线程池中提交任务,那么即使线程池中存在空闲线程,也会直接创建新线程来执行任务
  2. 如果线程池中运行的线程数大于corePoolSize,并且小于maximumPoolSize,那么只有当workQueue队列已满时,才会创建新的线程来执行新提交的任务
  3. 在调用ThreadPoolExecutor类的构造方法时,如果传递的corePoolSize和maximumPoolSize参数相同,那么创建的线程池大小是固定的,此时如果向线程池中提交任务,并且workQueue队列未满,就会将新提交的任务保存到workQueue队列中,等待空闲的线程,从workQueue队列中获取任务并执行
  4. 如果线程池中运行的线程数大于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
// 在Thread类中存在
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
// ThreadLocalMap的Entry是弱引用
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 {
// 问题场景:ThreadLocal被回收,但value仍被引用
public void problematicUsage() {
ThreadLocal<List<String>> local = new ThreadLocal<>();
local.set(new ArrayList<>(Collections.nCopies(1000000, "data"))); // 大对象

// ThreadLocal引用丢失
local = null; // 此时ThreadLocal可被GC

// 但ThreadLocalMap中的Entry.value仍然存在
// Thread -> ThreadLocalMap -> Entry -> value (强引用链)
// 造成内存泄漏
}

// 正确做法
public void correctUsage() {
ThreadLocal<List<String>> local = new ThreadLocal<>();
try {
local.set(new ArrayList<>());
// 使用local进行业务逻辑
} 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 {
// 每个任务都会在ThreadLocal中存储对象
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()); // 输出: null
// 为什么是null?因为线程池中的线程不是在设置CONTEXT后创建的
});

Thread.sleep(100);

// 第二个请求
CONTEXT.set("Request-2");
executor.submit(() -> {
System.out.println("Task2: " + CONTEXT.get()); // 可能输出: Request-1
// 复用了之前的线程,保留了旧值
});

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() {
// 场景1:先创建线程,后设置值
Thread thread1 = new Thread(() -> {
System.out.println("Thread1: " + INHERITABLE.get()); // null
});

INHERITABLE.set("ParentValue"); // 设置在线程创建之后
thread1.start(); // 无法继承

// 场景2:先设置值,后创建线程
INHERITABLE.set("ParentValue2");
Thread thread2 = new Thread(() -> {
System.out.println("Thread2: " + INHERITABLE.get()); // ParentValue2
});
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) {
// 默认是浅拷贝,子线程和父线程共享同一个List对象
// return 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使用建议

  1. 总是在finally块中调用remove() ,特别是在线程池环境中
  2. 避免在ThreadLocal中存储大对象 ,减少内存泄漏风险
  3. 使用static final修饰ThreadLocal变量 ,避免创建多个实例
  4. 考虑使用try-with-resources模式封装ThreadLocal操作

线程池配置建议

  1. 根据任务类型合理设置线程数 :I/O密集型和CPU密集型采用不同策略
  2. 选择合适的阻塞队列 :有界队列防止内存溢出
  3. 自定义ThreadFactory :设置有意义的线程名称,便于问题排查
  4. 监控线程池状态 :定期检查活跃线程数、队列大小等指标
  5. 优雅关闭线程池 :先调用shutdown(),等待一定时间后再调用shutdownNow()

InheritableThreadLocal使用建议

  1. 避免在线程池环境中使用 ,考虑使用其他上下文传递方案
  2. 重写childValue方法实现深拷贝,避免父子线程间的数据污染
  3. 注意继承时机 ,确保在创建子线程前设置父线程的值
  4. 考虑使用TransmittableThreadLocal等第三方库来解决线程池场景下的上下文传递问题

线程相关
http://gadoid.io/2025/08/06/线程相关/
作者
Codfish
发布于
2025年8月6日
许可协议