ThreadLocal

ThreadLocal 为线程局部变量,或许将它命名为ThreadLocalVariable更为合适。其主要作用就是实现线程本地存储功能,通过线程本地资源隔离,解决多线程并发场景下线程安全问题

如果没有 ThreadLocal 的话,就需要在各线程执行代码内部都加上大量重复代码【相同变量定义】。使用 ThreadLocal 的话,代码上复用一份,但实际各自线程维护各自局部变量数据

Java 中的 ThreadLocal 和 InheritableThreadLocal 都是用来实现线程本地存储的,它们都是线程局部变量。ThreadLocal 为每个线程提供了一个独立的变量副本,而 InheritableThreadLocal 则允许在子线程中访问父线程中设置的值。12

ThreadLocal 是一个本地线程副本变量工具类,主要用于将私有线程和该线程存放的副本对象做一个映射,各个线程之间的变量互不干扰,在高并发场景下,可以实现无锁化的状态共享。1

InheritableThreadLocal

子线程想使用父线程的变量,如果没有 InheritableThreadLocal 的话,就需要在各子线程中复制一份变量。使用 ThreadLocal InheritableThreadLocal,子线程自动拥有父线程变量的副本

InheritableThreadLocal 继承了 ThreadLocal,此类扩展了 ThreadLocal 以提供从父线程到子线程的值的继承:当创建子线程时,子线程接收父线程具有的所有可继承线程局部变量的初始值。2

ThreadLocal 和 InheritableThreadLocal 的区别在于:ThreadLocal 只能在当前线程中共享值,而 InheritableThreadLocal 可以在当前线程和子线程之间共享值。

InheritableThreadLocal 示例

  • 主线程存放数据,后面子线程读取数据,达到「共享数据」的效果,而不需要传参方式传递数据
  • 主线程先放数据,创建子线程后,子线程会复制一份数据。子线程修改数据对主线程的数据无影响
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package org.jeecg.common.util;

import cn.hutool.core.thread.threadlocal.NamedInheritableThreadLocal;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Map;

/**
* 主线程放数据,主/子线程取数据
* @author wangwz
* @date 2023/5/22
*/
@Slf4j
public class MainThreadShareDataUtil {
private static final ThreadLocal<Map<String, Object>> shareData = new NamedInheritableThreadLocal<>("shareData");

// 主线程添加共享数据
public static synchronized void put(String key, Object value) {
Map<String, Object> dataMap = shareData.get();
if(dataMap == null) {
dataMap = new HashMap<>();
shareData.set(dataMap);
}
dataMap.put(key, value);
}

// 主/子线程获取共享数据
public static Object get(String key) {
Map<String, Object> dataMap = shareData.get();
if(dataMap == null) return null;

return dataMap.get(key);
}

// 主线程清除共享数据
public static void remove() {
shareData.remove();
}

public static void main(String[] args) {
put("name", "zkkk"); // 主线程放数据
put("age", "122"); // 主线程放数据
put("class", "ddd"); // 主线程放数据

System.out.println(get("name")); // 主线程取数据
System.out.println(get("age")); // 主线程取数据
new Thread(()->{
System.out.println(get("name")); // 子线程取数据
System.out.println(get("age")); // 子线程取数据
}).start();
new Thread(()->{
System.out.println(get("class")); // 子线程取数据
}).start();
}
}