# 5.1 常用API以及使用
ThreadLocal的API文档如下图5-1所示,主要的方法有get、initialValue、remove、set和withInitial等5个方法。
图5-1 ThreadLocal的API
# 5.1.1 常用API
- initialValue()
ThreadLocal提供了两种实例化的方式: 继承ThreadLocal类,并重写initialValue()方法来定义初始化逻辑; 创建ThreadLocal的匿名子类,并在其构造器中初始化。 以下是两种方式的示例代码:
// 方式一:使用initialValue()方法初始化
public class MyThreadLocal extends ThreadLocal<String> {
@Override
protected String initialValue() {
return "Initial Value";
}
}
// 方式二:创建匿名子类并在构造器中初始化
ThreadLocal<String> myThreadLocal = new ThreadLocal<String>() {
@Override
protected String initialValue() {
return "Initial Value";
}
};
// 或者直接在创建时初始化
ThreadLocal<String> myThreadLocal = ThreadLocal.withInitial(() -> "Initial Value");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
withInitial()方法是Java8引入的一个简化的构造方法,允许使用Lambda表达式来赋值。
- get()
要从ThreadLocal中获取值,可以调用get方法:
MyThreadLocal myThreadLocal = new MyThreadLocal();
// 获取当前线程本地的值,初次调用会触发初始化
String value = myThreadLocal.get();
1
2
3
2
3
- remove()
要从ThreadLocal中删除值,可以调用remove方法:
myThreadLocal.remove();
1
- set()
设置当前线程的线程局部变量的值
myThreadLocal.set("New Value");
1
# 5.1.2 基本使用
下面的示例来说明ThreadLocal的基本使用。
public class ThreadLocalExample {
// 创建一个ThreadLocal变量来存储线程的ID
private static final ThreadLocal<Integer> threadId = new ThreadLocal<>() {
@Override
protected Integer initialValue() {
// 初始值设置为当前线程的ID
return Thread.currentThread().getId();
}
};
public static void main(String[] args) throws InterruptedException {
// 创建并启动几个线程
for (int i = 0; i < 5; i++) {
new Thread(() -> {
// 获取并打印当前线程的 ID
System.out.println("Thread ID: " + threadId.get());
}).start();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
在这个示例中,每个线程都会打印其自己的线程ID,而不是其他线程的ID。
ThreadLocal的应用场景主要分为两类:
- 避免对象在方法之间层层传递,打破层次间约束
例如请求调用链的唯一traceId,在很多地方都需要用到,层层往下传递,比较麻烦。 这时候就可以把traceId放到ThreadLocal中,在需要的地方可以直接获取。
- 拷贝对象副本,减少初始化操作,并保证线程安全
比如数据库连接、Spring事务管理和SimpleDataFormat格式化日期等场景,都是使用的ThreadLocal, 即避免每个方法都初始化一个对象,又保证了多线程下的线程安全。
使用ThreadLocal保证SimpleDataFormat格式化日期的线程安全,代码如下。
public class ThreadLocalDemo {
// 创建ThreadLocal
static ThreadLocal<SimpleDateFormat> threadLocal =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
public static void main(String[] args) {
IntStream.range(0, 5).forEach(i -> {
// 创建5个线程,分别从threadLocal取出SimpleDateFormat,然后格式化日期
new Thread(() -> {
try {
System.out.println(threadLocal.get().parse("2024-03-29 15:11:07"));
} catch (ParseException e) {
throw new RuntimeException(e);
}
}).start();
});
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19