ThreadLocal如何在父子线程及线程池中传递
ThreadLocal如何在父子线程及线程池中传递
ThreadLocal如何在父子线程及线程池中传递目录
子线程如何获取父线程的本地变量?
case
解决问题
子线程获取父线程ThreadLocal值
InheritableThreadLocal
问题
子线程如何获取父线程的本地变量?
子线程提交任务时,获取父线程ThreadLocal的值
case
之前遇到过多个线程都需要使用SimpleDataFormat对象来做日期格式化,当时第一时间想到的就是定义一个static的SimpleDataFormat让多线程共享。但是很显然这样做就是在写bug,因为SimpleDataFormat不是线程安全的,势必会出现并发问题。
解决问题
解决并发问题有很多种
- 悲观锁:使用简单,但锁粒度比较大,对读和写一视同仁
- 乐观锁:写少读多
- 线程本地变量:提供线程内部存储变量能力,线程私有,不会出现共享问题
- 等等...
子线程获取父线程ThreadLocal值
InheritableThreadLocal
jdk提供了InheritableThreadLocal
InheritableThreadLocal继承自ThreadLocal
1.当ThreadLocal使用方法存储的是基本变量时
public static ThreadLocal<Integer> threadLocal = new InheritableThreadLocal<>();
2.如果是引用变量
继续使用InheritableThreadLocal,父子线程就又存在多线程的问题
原理不做真如分析,跟InheritableThreadLocal的这个方法有关
由于直接返回入参,没有进行深拷贝,所以存在父子线程共享变量的问题
/** * Computes the child's initial value for this inheritable thread-local * variable as a function of the parent's value at the time the child * thread is created. This method is called from within the parent * thread before the child is started. * <p> * This method merely returns its input argument, and should be overridden * if a different behavior is desired. * * @param parentValue the parent thread's value * @return the child thread's initial value */ protected T childValue(T parentValue) { return parentValue; }
这个时候我们就需要区重写这个方法达到深拷贝的目的(采用序列化/反序列化的方法)
public class MyInheritableThreadLocal<T> extends InheritableThreadLocal<T>{ @Override protected T childValue(T parentValue){ String s = JSONObject.toJSONString(parentValue); return (T)JSONObject.parseObject(s,parentValue.getClass()); } }
问题
不管是InheritableThreadLocal还是实现MyInheritableThreadLocal都是现成创建之初就已经创建好的。但如果是线程池的场景,是维持了一个若干线程的,有可能创建好了就不会再重新创建了,也就是线程复用。这个时候InheritableThreadLocal的值被改变了,要是再次有任务提交。那么使用的共享值就不再是拷贝自父线程的值了,也无法再次获取父线程的本地变量。
解决方法
TransmittableThreadLocal是阿里开源用来解决这一问题的扩展,原理就不想写的太深了,以后有机会继续研究
public class MyInheritableThreadLocal<T> extends TransmittableThreadLocal<T>{ @Override protected T copy(T parentValue){ String s = JSONObject.toJSONString(parentValue); return (T)JSONObject.parseObject(s,parentValue.getClass()); } }