}
主线程中定义一个静态变量util,初始值是123,在子线程和主线程中分别打印ThreadLocal变量值。
public class ThreadLocalExample {
private static Logger logger = LoggerFactory.getLogger(ThreadLocalExample.class);
private static ThreadLocalUtil util = new ThreadLocalUtil<>(123);
public static void main(String[] args) {
new Thread(() -> logger.info("Thread name = {}, thread variable is {}", Thread.currentThread().getName(), util.getValue())).start();
logger.info("Thread name = {}, thread variable is {}", Thread.currentThread().getName(), util.getValue());
}
}
示例结果如下,子线程输出的是子线程初始值null,主线程输出的是初始值123。另外,这里**不保证线程的有序性**,只是简易示例。
![在这里插入图片描述](https://img-blog.csdnimg.cn/341bd44e08284388971f5b61448978f8.png)
## InheritableThreadLocal(ITL)示例
InheritableThreadLocal主要用于子线程创建时,需要自动继承父线程的ThreadLocal变量时使用。
把上面的代码稍微改造一下,
/**
* ThreadLocal工具类
*
* @param 范型T
*/
public final class ThreadLocalUtil {
private final ThreadLocal THREAD_LOCAL = new InheritableThreadLocal<>();
/\*\*
* 无参构造函数
*/
public ThreadLocalUtil() {
}
/\*\*
* 有参构造函数
*
* @param t
*/
ThreadLocalUtil(T t) {
THREAD_LOCAL.set(t);
}
/\*\*
* 从ThreadLocalMap中获取当前线程变量
*
* @return 变量值
*/
public T getValue() {
return THREAD_LOCAL.get();
}
/\*\*
* ThreadLocalMap中写入当前线程变量
*
* @param t 入参
* @return 当前写入值
*/
public T setValue(T t) {
THREAD_LOCAL.set(t);
return t;
}
/\*\*
* 从ThreadLocalMap中删除当前线程变量
*/
public void removeValue() {
THREAD_LOCAL.remove();
}
}
代码不变,子线程中输出的ThreadLocal变量继承了主线程中的ThreadLocal变量值,
![在这里插入图片描述](https://img-blog.csdnimg.cn/f2834b0dc2a54c8e9eb1532c863ead70.png)
## TransmittableThreadLocal(TTL)示例
TransmittableThreadLocal,简称TTL,它不同于ITL,它能实现池化线程间传递。直接看官方的时序图,
![在这里插入图片描述](https://img-blog.csdnimg.cn/b2a3c358036b4bf195ddd8894eca63e1.png)
大部分过程都依赖于 TransmittableThreadLocal 或 TransmittableThreadLocal 中声明的静态工具类 Transmitter 。Transmitter 主要负责 ThreadLocal 的管理和值的传递。
使用前需要导入依赖,
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.12.6</version>
</dependency>
下面启了两个线程main\_01和main\_02作为父线程,线程池中执行的三个子线程拿到父线程设置的ttl变量,父线程再修改ttl值,然后线程池中的子线程继续读取ttl中的值。
import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.threadpool.TtlExecutors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TransmittableThreadLocalDemo {
/**
* 需要注意的是,使用TTL的时候,要想传递的值不出问题,线程池必须得用TTL加一层代理
*/
private static ExecutorService executorService = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(2));
private static ThreadLocal ttl = new TransmittableThreadLocal<>();
public static void main(String[] args) {
new Thread(() -> {
String mainThreadName = "main\_01";
ttl.set(1);
executorService.execute(() -> {
sleep(1L);
System.out.println(String.format("本地变量改变之前(1), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
});
executorService.execute(() -> {
sleep(1L);
System.out.println(String.format("本地变量改变之前(1), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
});
executorService.execute(() -> {
sleep(1L);
System.out.println(String.format("本地变量改变之前(1), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
});
sleep(1L); //确保上面的会在tl.set执行之前执行
ttl.set(2); // 等上面的线程池第一次启用完了,父线程再给自己赋值
executorService.execute(() -> {
sleep(1L);
System.out.println(String.format("本地变量改变之后(2), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
});
executorService.execute(() -> {
sleep(1L);
System.out.println(String.format("本地变量改变之后(2), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
});
executorService.execute(() -> {
sleep(1L);
System.out.println(String.format("本地变量改变之后(2), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
});
System.out.println(String.format("线程名称-%s, 变量值=%s", Thread.currentThread().getName(), ttl.get()));
}).start();
new Thread(() -> {
String mainThreadName = "main\_02";
ttl.set(3);
executorService.execute(() -> {
sleep(1L);
System.out.println(String.format("本地变量改变之前(3), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
});
executorService.execute(() -> {
sleep(1L);
System.out.println(String.format("本地变量改变之前(3), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
});
executorService.execute(() -> {
sleep(1L);
System.out.println(String.format("本地变量改变之前(3), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
});
sleep(1L); //确保上面的会在tl.set执行之前执行
ttl.set(4); // 等上面的线程池第一次启用完了,父线程再给自己赋值
executorService.execute(() -> {
sleep(1L);
System.out.println(String.format("本地变量改变之后(4), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
});
executorService.execute(() -> {
sleep(1L);
System.out.println(String.format("本地变量改变之后(4), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
});
executorService.execute(() -> {
sleep(1L);
System.out.println(String.format("本地变量改变之后(4), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
});
System.out.println(String.format("线程名称-%s, 变量值=%s", Thread.currentThread().getName(), ttl.get()));
}).start();
}
private static void sleep(long longTime) {
try {
Thread.sleep(longTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
最终结果如下,线程池中子线程拿到的是父线程中设置的值。
![在这里插入图片描述](https://img-blog.csdnimg.cn/84f356ed125c4b5da3fd29f4e7359507.png)
## 总结
![img](https://img-blog.csdnimg.cn/img_convert/a0a75e1535dc1cc05c9752f5cb5c1fc8.png)
![img](https://img-blog.csdnimg.cn/img_convert/498cb27f10c0eeed0a7387d8cd209103.png)
![img](https://img-blog.csdnimg.cn/img_convert/4b6e0a6a5812f866e1b6fe12af6869d2.png)
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!**
da3fd29f4e7359507.png)
## 总结
[外链图片转存中...(img-7UiyaDEU-1726103571859)]
[外链图片转存中...(img-HLJinvhx-1726103571860)]
[外链图片转存中...(img-kjR5jeSl-1726103571860)]
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!**