细数ThreadLocal三大坑,内存泄露仅是小儿科( 三 )
可以看到 , 我们初始化线程池的时候指定如果线程池满 , 则新提交的任务转为串行执行 , 那我们之前的写法就会有问题了 , 串行执行的时候调用ContextHolder.remove;会将主线程的上下文也清理 , 即使后面线程池继续并行工作 , 传给子线程的上下文也已经是null了 , 而且这样的问题很难在预发测试的时候发现 。
如果ThreadLocal碰到并行流 , 也会有很多有意思的事情发生 , 比如有下面的代码:
class ParallelProcessor<T> { public void process(List<T> dataList) { // 先校验参数 , 篇幅限制先省略不写 dataList.parallelStream.forEach(entry -> { doIt; }); } private void doIt { String session = ContextHolder.get; // do something }}
这段代码很容易在线下测试的过程中发现不能按照预期工作 , 因为并行流底层的实现也是一个ForkJoin线程池 , 既然是线程池 , 那ContextHolder.get可能取出来的就是一个null 。 我们顺着这个思路把代码再改一下:
class ParallelProcessor<T> { private String session; public ParallelProcessor(String session) { this.session = session; } public void process(List<T> dataList) { // 先校验参数 , 篇幅限制先省略不写 dataList.parallelStream.forEach(entry -> { try { ContextHolder.set(session); // 业务处理 doIt; } catch (Exception e) { // log it } finally { ContextHolder.remove; } }); } private void doIt { String session = ContextHolder.get; // do something }}
【细数ThreadLocal三大坑,内存泄露仅是小儿科】修改完后的这段代码可以工作吗?如果运气好 , 你会发现这样改又有问题 , 运气不好 , 这段代码在线下运行良好 , 这段代码就顺利上线了 。 不久你就会发现系统中会有一些其他很诡异的bug 。 原因在于并行流的设计比较特殊 , 父线程也有可能参与到并行流线程池的调度 , 那如果上面的process方法被父线程执行 , 那么父线程的上下文会被清理 。 导致后续拷贝到子线程的上下文都为null , 同样产生丢失上下文的问题 。
- 三星|流畅用三年,两千价位机型,为什么说这款最值得买?
- |内蒙古天空出现“三个太阳”,古人称是“不祥之兆”,真的吗?
- |如何区分三相电源的相序?
- 霍金|霍金留下三个预言,每一个都事关人类安危,第一个已经开始应验
- 高通骁龙|全部配备骁龙778G与5000毫安电池,这三款手机高度‘套娃’
- 华为|华三很难超越华为的体量
- 魅族|从3999降至2499,8+256GB、三星定制屏,魅族也有高性价比
- |买千元机别胡乱跟风,这三款才是内行人最爱,配置强悍外观精致
- 三星|真安卓机皇诞生?103万跑分+双曲面2K屏幕,三星S22太激进了
- 市场规模|人与自然和谐共生教育:陆生动物(三)?