谁给个ThreadLocal的套期保值简单例子子

在系统开发过程中常使用ThreadLocal进行传遞日志的RequestId由此来获取整条请求链路。然而当线程中开启了其他的线程此时ThreadLocal里面的数据将会出现无法获取/读取错乱,甚至还可能会存茬内存泄漏等问题下面用代码来演示一下这个问题。

ThreadLocal的子类InheritableThreadLocal其实已经帮我们处理好了通过这个组件可以实现父子线程之间的数据传递,在子线程中能够父线程中的ThreadLocal本地变量

代码的意思是在Thread获取先父亲线程parent(即要创建子线程的当前这个线程)。当父亲线程中对inherThreadLocals进行了赋徝就会把当前线程的本地变量(也就是父线程的inherThreadLocals)进行createInheritedMap方法操作。查看源码createInheritedMap方法源码可知此操作就是将赋线程的threadLocalMap传递给子线程。

我们寫个代码测试一下:

看起来似乎真的是解决了我们无法传递的问题

四、真的就这么美好么?我们来和线程池搭配一下

测试结果显示两次賦值得到的结果还是第一次的值!为什么?

其实原因也很简单我们的线程池会缓存使用过的线程。当线程需要被重复利用的时候并鈈会再重新执行init()初始化方法,而是直接使用已经创建过的线程所以这里的值不会二次产生变化,那么该怎么做到真正的父子线程数据传遞呢

五、真正的解决方案:阿里的了解一下

JDK的类可以完成父线程到子线程的值传递。但对于使用线程池等会池化复用线程的组件的情况线程由线程池创建好,并且线程是池化起来反复使用的;这时父子线程关系的ThreadLocal值传递已经没有意义应用需要的实际上是把任务提交给線程池时的ThreadLocal值传递到任务执行时。

这里有一个很重要的变量holder:源码如下

主要的几个相关方法:源码如下

1. get方法调用时先获取父亲的相关数據判断是否有数据,然后在holder中把自身也给加进去

2. set方法调用时,先在父亲中设置再本地判断是holder否为删除或者是新增数据。

3. remove调用时先删除自身,再删除父亲中的数据删除也是直接以自身this作为变量Key。

采用包装的形式来处理线程池中的线程不会执行初始化的问题源码如下:

2. 备份线程本地数据

4. 还原线程本地数据

2. 进行迭代,数据在captured中不存在但是holder中存在,说明是后来加进去的进行删除。

2. backup中不存在holder中存在,說明是后面加进去的进行删除还原操作。

3. 再将backup设置到当前线程中

下面是几个典型场景例子。

2. 日志收集记录系统上下文

3. 应用容器或上层框架跨应用代码给下层SDK传递信息

}
//final 可用volatile 该对象的创建表示默认主線程创建了一条父线程

 代码说明:由于存储在本地变量中的值都是不相关的。当一个新的线程被创建出来它就会获得一个新的包含initialValue()值的存储槽。

InheritableThreadLocal类的源代码如下(为了查看方面已删去部分注释):
}

我要回帖

更多关于 套期保值简单例子 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信