本文主要是介绍线程间数据传递之ThreadLocal、InheritableThreadLocal、TransmittableThreadLocal,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
线程间数据传递之ThreadLocal、InheritableThreadLocal、TransmittableThreadLocal
1、ThreadLocal介绍
spring 中基于 ThreadLocal 来实现事务。
多线程 访问同一个共享变量的时候容易出现并发问题,ThreadLocal是除了加锁这种同步方式之外的一种保证
规避多线程访问出现线程不安全的方法,当我们在创建一个变量后,如果每个线程对其进行访问的时候访问的都是
线程自己的变量这样就不会存在线程不安全问题。在实际多线程操作的时候,操作的是自己本地内存中的变量,从
而规避了线程安全问题。
ThreadLocal又叫做线程本地变量是为每一个Thread创建的一个变量的副本,每个线程都可以在内部访问到这个副
本。通过这种方式我们在一个线程的生命周期以内安全的访问这个变量,不用担心被其他线程所污染。这是它相对
于全局变量所带来的优势。
package com.example.threadlocalandother;/*** @author tom*/
public class ThreadLocalDemo {private ThreadLocal<Long> threadLocal = new ThreadLocal<>();private void fun1() {threadLocal.set(System.nanoTime());System.out.println("fun1:" + threadLocal.get());fun2();}private void fun2() {System.out.println("fun2:" + threadLocal.get());fun3();}private void fun3() {System.out.println("fun3:" + threadLocal.get());threadLocal.remove();}public static void main(String[] args) {ThreadLocalDemo demo = new ThreadLocalDemo();demo.fun1();}
}
# 程序输出
fun1:1511680153700
fun2:1511680153700
fun3:1511680153700
我们在先创建了一个本地变量threadLocal 在fun1中set值,在其余的方法中我们并没有通过方法传递的方式显示
的将值传递给其他方法,仅仅是通过threadLocal变量get的方式就可以获取到我们所需要的变量,从而实现了变量
的跨方法的传递。可能你觉得这样写没什么好处,我不用threadLocal直接用个全局变量照样可以实现数据的传
递,我们可以改造一下fun1方法。
package com.example.threadlocalandother;/*** @author tom*/
public class ThreadLocalDemo1 {private ThreadLocal<Long> threadLocal = new ThreadLocal<>();private void fun1() throws InterruptedException {threadLocal.set(System.nanoTime());System.out.println("fun1:" + threadLocal.get());final Thread t1 = new Thread(() -> {System.out.println("t1:" + threadLocal.get());}, "t1");t1.start();t1.join();fun2();}private void fun2() {System.out.println("fun2:" + threadLocal.get());fun3();}private void fun3() {System.out.println("fun3:" + threadLocal.get());threadLocal.remove();}public static void main(String[] args) throws InterruptedException {ThreadLocalDemo1 threadLocalDemo1 = new ThreadLocalDemo1();threadLocalDemo1.fun1();}
}
# 程序输出
fun1:3554613378000
t1:null
fun2:3554613378000
fun3:3554613378000
按照常理来想那么我们t1线程内也应该能获取到数据,但是结果大相径庭。实际上 threadLocal 对象只是作为了一
个 key,而真正存储数据的是每个线程自身的 thread 内持有的一个 ThreadLocalMap 的对象,而我们的 t1 线程
自然就不能获取到数据。如果使用后不 remove 可能会有内存泄漏的风险!
package com.example.threadlocalandother;/*** @author tom*/
public class ThreadLocalDemo2 {private ThreadLocal<Long> threadLocal = new ThreadLocal<>();public void run() {for (int i = 0; i < 10; i++) {new Thread(new Runnable() {@Overridepublic void run() {threadLocal.set(System.nanoTime());Long aLong = threadLocal.get();threadLocal.remove();System.out.println(aLong);}}).start();}}public static void main(String[] args) {ThreadLocalDemo2 threadLocalDemo2 = new ThreadLocalDemo2();threadLocalDemo2.run();}
}
# 程序输出
3042907777600
3042907791300
3042908175100
3042908275900
3042908395900
3042908245300
3042908563900
3042908182800
3042908589200
3042908716700
package com.example.threadlocalandother;/*** @author tom*/
public class ThreadLocalDemo3 {/*** 创建ThreadLocal变量*/private static final ThreadLocal<Integer> LOCAL_VARIABLE = new ThreadLocal<>();static void print(String str) {// 打印当前线程本地内存中localVariable变量的值System.out.println(str + ": " + LOCAL_VARIABLE.get());// 清除当前线程本地内存中的localVariable变量LOCAL_VARIABLE.remove();}public static void main(String[] args) {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {// 设置线程1中本地变量的值LOCAL_VARIABLE.set(100);print("Thread 1");System.out.println("After remove: " + LOCAL_VARIABLE.get());}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {// 设置线程2中本地变量的值LOCAL_VARIABLE.set(200);print("Thread 2");System.out.println("After remove: " + LOCAL_VARIABLE.get());}});t1.start();t2.start();}
}
# 程序输出
Thread 1: 100
Thread 2: 200
After remove: null
After remove: null
package com.example.threadlocalandother;/*** @author tom*/
public class UserContext implements AutoCloseable {private final ThreadLocal<String> ctx = new ThreadLocal<>();public UserContext(String user) {ctx.set(user);}public String currentUser() {return ctx.get();}@Overridepublic void close() {ctx.remove();}public static void main(String[] args) {try (UserContext userContext = new UserContext("Bob")) {// 可任意调用userContext.currentUser():String currentUser = userContext.currentUser();System.out.println(currentUser);} // 在此自动调用UserContext.close()方法释放ThreadLocal关联对象catch (Exception e) {e.printStackTrace();}}
}
# 程序输出
Bob
使用ThreadLocal的最终目的还是为了得到安全的数据。
问题:同一个ThreadLocal变量在父线程中被设置值后,在子线程中是获取不到的,好在InheritableThreadLocal
可以解决这个问题。
2、InheritableThreadLocal介绍
以我们在最开始的demo中t1线程获取不到数据。但是如果我们有这种诉求,希望父线程能够向子线程传递数据
呢,那我们便可以用到InheritableThreadLocal。
如何解决子线程获取父线程的数据就要使用InheritableThreadLocal。
package com.example.threadlocalandother;/*** @author tom*/
public class InheritableThreadLocalDemo {private ThreadLocal<Long> inheritableThreadLocal = new InheritableThreadLocal<>();private void fun1() throws InterruptedException {inheritableThreadLocal.set(System.nanoTime());System.out.println("fun1:" + inheritableThreadLocal.get());final Thread t1 = new Thread(() -> {System.out.println("t1:" + inheritableThreadLocal.get());}, "t1");t1.start();t1.join();fun2();}private void fun2() {System.out.println("fun2:" + inheritableThreadLocal.get());fun3();}private void fun3() {System.out.println("fun3:" + inheritableThreadLocal.get());inheritableThreadLocal.remove();}public static void main(String[] args) throws InterruptedException {InheritableThreadLocalDemo inheritableThreadLocalDemo = new InheritableThreadLocalDemo();inheritableThreadLocalDemo.fun1();}
}
# 程序输出
fun1:7459897559400
t1:7459897559400
fun2:7459897559400
fun3:7459897559400
我们可以看到在t1线程中能够正确的获取到结果。
package com.example.threadlocalandother;/*** @author tom*/
public class InheritableThreadLocalDemo1 {private ThreadLocal<Integer> inheritableThreadLocal = new InheritableThreadLocal<>();public void run() {//给父类inheritableThreadLocals赋值inheritableThreadLocal.set(1);new Thread(new Runnable() {@Overridepublic void run() {//子线程同时去获取自己的inheritableThreadLocalsInteger integer = inheritableThreadLocal.get();System.out.println(integer);}}).start();Integer integer = inheritableThreadLocal.get();System.out.println(integer);inheritableThreadLocal.remove();}public static void main(String[] args) {InheritableThreadLocalDemo1 inheritableThreadLocalDemo1 = new InheritableThreadLocalDemo1();inheritableThreadLocalDemo1.run();}
}
# 程序输出
1
1
InheritableThreadLocal类**「继承」**了ThreadLocal类,并重写了childValue、getMap、createMap方法。
总之,InheritableThreadLocals类通过重写getMap和createMap两个方法将本地变量保存到了具体线程的
inheritableThreadLocals变量中,当线程通过InheritableThreadLocals实例的set或者get方法设置变量的时候,
就会创建当前线程的inheritableThreadLocals变量。而父线程创建子线程的时候,ThreadLocalMap中的构造函数
会将父线程的inheritableThreadLocals中的变量**「复制一份到子线程的inheritableThreadLocals」**变量中。
package com.example.threadlocalandother;import com.alibaba.fastjson.JSONObject;import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;/*** @author tom*/
public class UserInheritableThreadLocal {static InheritableThreadLocal<Map<String, String>> inheritableThreadLocal = new InheritableThreadLocal<>();public static void set(String key, String value) {Map<String, String> headers = new HashMap<>();headers.put(key, value);inheritableThreadLocal.set(headers);}public static Map<String, String> get() {return inheritableThreadLocal.get();}public static void remove() {if (inheritableThreadLocal != null) {inheritableThreadLocal.remove();}}public static void main(String[] args) {//设置值UserInheritableThreadLocal.set("name", "tom");Map<String, String> stringStringMap = UserInheritableThreadLocal.get();System.out.println(JSONObject.toJSONString(stringStringMap));// 创建线程池ExecutorService executorService = new ThreadPoolExecutor(5, 5, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(5));//使用线程池创建子线程,并在子线程获取executorService.submit(() -> {Map<String, String> stringMap = UserInheritableThreadLocal.get();System.out.println(JSONObject.toJSONString(stringMap));});UserInheritableThreadLocal.remove();executorService.shutdown();}
}
# 程序输出
{"name":"tom"}
{"name":"tom"}
package com.example.threadlocalandother;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** @author tom*/
public class InheritableThreadLocalDemo2 {// 创建一个InheritableThreadLocal来存储用户IDprivate static final InheritableThreadLocal<String> userIdThreadLocal = new InheritableThreadLocal<>();public static void main(String[] args) {// 模拟处理用户请求handleUserRequest("user123");}public static void handleUserRequest(String userId) {// 在处理请求前,设置当前线程的用户IDuserIdThreadLocal.set(userId);try {// 模拟一些业务逻辑doBusinessLogic();// 模拟异步处理任务ExecutorService executor = Executors.newSingleThreadExecutor();executor.submit(() -> {// 在子线程中也可以访问到父线程设置的用户 IDSystem.out.println("Processing task in child thread for user: " + userIdThreadLocal.get());});executor.shutdown();} finally {// 清除当前线程的用户 ID,防止内存泄漏userIdThreadLocal.remove();}}public static void doBusinessLogic() {// 在业务逻辑中可以访问到当前线程的用户 IDSystem.out.println("Processing business logic for user: " + userIdThreadLocal.get());}
}
# 程序输出
Processing business logic for user: user123
Processing task in child thread for user: user123
需要在实际使用完毕的时候,及时调用remove方法避免内存泄漏。
inheritableThreadLocal是线程安全的吗?
package com.example.threadlocalandother;/*** @author tom*/
public class InheritableThreadLocalDemo3 {private ThreadLocal<Person> inheritablethreadlocal = new InheritableThreadLocal<>();private void fun1() throws InterruptedException {final Person person = new Person();person.setName("张三");inheritablethreadlocal.set(person);System.out.println("fun1:" + inheritablethreadlocal.get());final Thread t1 = new Thread(() -> {Person p = (Person) inheritablethreadlocal.get();p.setName("李四");System.out.println("t1:" + inheritablethreadlocal.get());}, "t1");t1.start();t1.join();fun2();}private void fun2() {System.out.println("fun2:" + inheritablethreadlocal.get());fun3();}private void fun3() {System.out.println("fun3:" + inheritablethreadlocal.get());inheritablethreadlocal.remove();}public static void main(String[] args) throws InterruptedException {InheritableThreadLocalDemo3 inheritableThreadLocalDemo3 = new InheritableThreadLocalDemo3();inheritableThreadLocalDemo3.fun1();}
}class Person {String name;public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Person{" + "name='" + name + '\'' + '}';}
}
# 程序输出
fun1:Person{name='张三'}
t1:Person{name='李四'}
fun2:Person{name='李四'}
fun3:Person{name='李四'}
如果它是线程安全的那么,在t1线程中的修改应该不能够影响到其他线程中的值,显而易见它并不是线程安全的。
解决方法是重写childValue方法,在里面做一个深拷贝就可以了。
package com.example.threadlocalandother;/*** @author tom*/
public class InheritableThreadLocalDemo4 {private ThreadLocal<Person> inheritablethreadlocal = new InheritableThreadLocal() {@Overrideprotected Object childValue(Object parentValue) {Person p = (Person) parentValue;final Person child = new Person();child.setName(p.getName());return child;}};private void fun1() throws InterruptedException {final Person person = new Person();person.setName("张三");inheritablethreadlocal.set(person);System.out.println("fun1:" + inheritablethreadlocal.get());final Thread t1 = new Thread(() -> {Person p = (Person) inheritablethreadlocal.get();p.setName("李四");System.out.println("t1:" + inheritablethreadlocal.get());}, "t1");t1.start();t1.join();fun2();}private void fun2() {System.out.println("fun2:" + inheritablethreadlocal.get());fun3();}private void fun3() {System.out.println("fun3:" + inheritablethreadlocal.get());inheritablethreadlocal.remove();}public static void main(String[] args) throws InterruptedException {InheritableThreadLocalDemo4 inheritableThreadLocalDemo4 = new InheritableThreadLocalDemo4();inheritableThreadLocalDemo4.fun1();}
}
# 程序输出
fun1:Person{name='张三'}
t1:Person{name='李四'}
fun2:Person{name='张三'}
fun3:Person{name='张三'}
看起来似乎是解决了问题,结果也很美好。那么进一步思考下,我们这是在父线程给子线程传值,如果是两个不相
关的线程呢,比如说在线程池中会怎么样呢?创建线程如果是用显示创建使用inheritableThreadLocals是没有问
题。但如果是使用线程池就会有问题。
package com.example.threadlocalandother;import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;/*** @author tom*/
public class InheritableThreadLocalDemo5 {static ExecutorService executorService = new ThreadPoolExecutor(2, 2, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100));static ThreadLocal<Integer> inheritableThreadLocal = new InheritableThreadLocal<>();public static void main(String[] args) {System.out.println("主线程开始");for (int i = 0; i < 10; i++) {inheritableThreadLocal.set(i);System.out.println("主线程获取值:" + inheritableThreadLocal.get());executorService.execute(new RunnableDemo());inheritableThreadLocal.remove();}executorService.shutdown();}private static class RunnableDemo implements Runnable {@Overridepublic void run() {System.out.println("子线程获取值:" + inheritableThreadLocal.get());}}
}
# 程序输出
主线程开始
主线程获取值:0
主线程获取值:1
主线程获取值:2
主线程获取值:3
主线程获取值:4
子线程获取值:0
子线程获取值:1
主线程获取值:5
子线程获取值:1
子线程获取值:0
子线程获取值:1
主线程获取值:6
子线程获取值:0
子线程获取值:0
主线程获取值:7
主线程获取值:8
子线程获取值:0
子线程获取值:1
主线程获取值:9
子线程获取值:0
按照我们上文的分析,那也结果也应该是子线程跟主线程都会输出从0~9,但是结果却大相径庭。
package com.example.threadlocalandother;import java.util.concurrent.*;/*** @author tom*/
public class InheritableThreadLocalDemo6 {static ThreadLocal<Integer> threadLocal = new InheritableThreadLocal<>();static ExecutorService executorService = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100));static CountDownLatch firstCountDownLatch = new CountDownLatch(1);static CountDownLatch secondCountDownLatch = new CountDownLatch(1);public static void main(String[] args) throws InterruptedException {threadLocal.set(1);executorService.execute(() -> {System.out.println("子线程获取" + threadLocal.get());threadLocal.remove();System.out.println("异步任务1");firstCountDownLatch.countDown();});firstCountDownLatch.await();System.out.println("父线程获取" + threadLocal.get());threadLocal.set(2);executorService.execute(() -> {System.out.println("子线程获取" + threadLocal.get());threadLocal.remove();System.out.println("异步任务2");secondCountDownLatch.countDown();});secondCountDownLatch.await();System.out.println("父线程获取" + threadLocal.get());executorService.shutdown();}
}
# 程序输出
子线程获取1
异步任务1
父线程获取1
子线程获取null
异步任务2
父线程获取2
我们新创建了一个Runnable对象放入线程池中通过execute方法执行,会首先判断线程池的核心线程数有没有达
到最大,如果还没达到最大那么新启动一个work线程,如果达到最大,那么接着判断我们给的队列是否满了,如
果还未满就入队,如果已经满了继续判断最大线程数是否达到最大,如果还未达到最大则继续新启动一个work线
程,已经达到最大就执行拒绝策略。
当核心线程数还没用完的时候,会创建新的线程,那么InheritableThreadLocal的值就会从父线程里面copy,自然
是没有问题,当我们在子线程中操作InheritableThreadLocal是可以拿到数据。
但是如果有新的任务进来(核心线程池满了,队列没有满),只要核心线程有空闲,就会复用原先创建好的核心线
程,这个时候,如果上一个使用过这个线程的子线程修改了InheritableThreadLocal,那么当前的子线程在使用
InheritableThreadLocal就会有问题了。因为这次是没有重行创建新线程,那么InheritableThreadLocal还是之前
的InheritableThreadLocal。
InheritableThreadLocal的继承性是在new Thread创建子线程时候在构造函数内把父线程内线程变量拷贝到子线
程内部的。为了不在创建新线程耗费资源,我们一般会用线程池,线程池的线程会复用,那么线程中的
ThreadLocal便不对了,可能是旧的,因为线程是旧的。
如果我们想在线程池等复用线程的组建中,使用ThreadLocal值的传递功能,来解决异步执行时上下文传递,那么
应该如何处理呢?
3、TransmittableThreadLocal介绍
JDK的InheritableThreadLocal类可以完成父线程到子线程的值传递。但对于使用线程池等会池化复用线程的执行
组件的情况,线程由线程池创建好,并且线程是池化起来反复使用的;这时父子线程关系的ThreadLocal值传递已
经没有意义,应用需要的实际上是把 任务提交给线程池时的ThreadLocal值传递到 任务执行时。
TransmittableThreadLocal类继承并加强InheritableThreadLocal类,解决上述的问题。
InheritableThreadLocal为阿里开源的一个组件,所以我们在使用的时候需要添加如下依赖:
<dependency><groupId>com.alibaba</groupId><artifactId>transmittable-thread-local</artifactId><version>2.12.6</version>
</dependency>
示例:
package com.example.threadlocalandother;import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.TtlRunnable;import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;/*** @author tom*/
public class TransmittableThreadLocalDemo {static ExecutorService executorService = new ThreadPoolExecutor(2, 2, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100));static ThreadLocal<Integer> inheritableThreadLocal = new TransmittableThreadLocal<>();public static void main(String[] args) {System.out.println("主线程开始");for (int i = 0; i < 10; i++) {inheritableThreadLocal.set(i);System.out.println("主线程获取值:" + inheritableThreadLocal.get());executorService.execute(TtlRunnable.get(new RunnableDemo()));inheritableThreadLocal.remove();}executorService.shutdown();}private static class RunnableDemo implements Runnable {@Overridepublic void run() {System.out.println("子线程获取值:" + inheritableThreadLocal.get());}}
}
# 程序输出
主线程开始
主线程获取值:0
主线程获取值:1
主线程获取值:2
主线程获取值:3
子线程获取值:0
子线程获取值:1
主线程获取值:4
子线程获取值:2
子线程获取值:3
主线程获取值:5
子线程获取值:4
子线程获取值:5
主线程获取值:6
主线程获取值:7
子线程获取值:6
主线程获取值:8
子线程获取值:7
主线程获取值:9
子线程获取值:8
子线程获取值:9
这样我们就可以在子线程中正确的获取到想要的结果了。
package com.example.threadlocalandother;import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.TtlRunnable;import java.util.concurrent.*;/*** @author tom*/
public class TransmittableThreadLocalDemo2 {static ThreadLocal<Integer> threadLocal = new TransmittableThreadLocal<>();static ExecutorService executorService = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100));static CountDownLatch firstCountDownLatch = new CountDownLatch(1);static CountDownLatch secondCountDownLatch = new CountDownLatch(1);public static void main(String[] args) throws InterruptedException {threadLocal.set(1);executorService.execute(TtlRunnable.get(() -> {System.out.println("子线程获取" + threadLocal.get());threadLocal.remove();System.out.println("异步任务1");firstCountDownLatch.countDown();}));firstCountDownLatch.await();System.out.println("父线程获取" + threadLocal.get());threadLocal.set(2);//就算上面线程把数据删了,而且还复用了上面的线程,也不影响。executorService.execute(TtlRunnable.get(() -> {System.out.println("子线程获取" + threadLocal.get());threadLocal.remove();System.out.println("异步任务2");secondCountDownLatch.countDown();}));secondCountDownLatch.await();System.out.println("父线程获取" + threadLocal.get());executorService.shutdown();}
}
# 程序输出
子线程获取1
异步任务1
父线程获取1
子线程获取2
异步任务2
父线程获取2
package com.example.threadlocalandother;import com.alibaba.ttl.TransmittableThreadLocal;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** @author tom*/
public class TransmittableThreadLocalDemo3 {private static final TransmittableThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();public static void main(String[] args) throws InterruptedException {ExecutorService executorService = Executors.newFixedThreadPool(2);executorService.submit(() -> {threadLocal.set("Hello, World!");try {doBusinessLogic();} finally {threadLocal.remove();}});executorService.submit(() -> {try {doBusinessLogic();} finally {threadLocal.remove();}});executorService.shutdown();}public static void doBusinessLogic() {// 在业务逻辑中可以访问到当前线程的变量值System.out.println("Processing business logic with value: " + threadLocal.get());}}
# 程序输出
Processing business logic with value: Hello, World!
Processing business logic with value: null
自己实现一个类似于TransmittableThreadLocal的功能:
package com.example.threadlocalandother;import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Supplier;/*** @author tom*/
public class TransmittableThreadLocalDemo4 {private static ThreadLocal<String> contextHolder = new ThreadLocal<>();public static <T> CompletableFuture<T> invokeToCompletableFuture(Supplier<T> supplier) {// 第一步String context = contextHolder.get();Supplier<T> newSupplier = () -> {//第二步String origin = contextHolder.get();try {contextHolder.set(context);// 第三步return supplier.get();} finally {// 第四步contextHolder.set(origin);}};return CompletableFuture.supplyAsync(newSupplier);}public static void main(String[] args) throws ExecutionException, InterruptedException {contextHolder.set("main");CompletableFuture<String> context = invokeToCompletableFuture(() -> TransmittableThreadLocalDemo4.contextHolder.get());System.out.println(context.get());}
}
# 程序输出
main
总得来说,就是在将异步任务派发给线程池时,「对其做一下上下文传递的处理。」
第一步:主线程获取上下文,传递给任务暂存。之后的操作都将是异步执行线程操作的。
第二步:异步执行线程将原有上下文取出,暂时保存。并将主线程传递过来的上下文设置。
第三步:执行异步任务。
第四步:将原有上下文设置回去。
可以看到一般在异步线程执行完任务之后不会直接进行remove,而是一开始取出原上下文(可能为 「NULL」,
也可能是线程创建时InheritableThreadLocal 继承过来的值。当然后续也会被清除的),并在任务执行
「结束重新放回」。这样的方式可以说是异步 「ThreadLocal 传递的标准范式」
这样子既起到了显式清除主线程带来的上下文,也避免了如果线程池的拒绝策略为 CallerRunsPolicy ,后续
处理时上下文丢失的问题。
这篇关于线程间数据传递之ThreadLocal、InheritableThreadLocal、TransmittableThreadLocal的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!