在实际的业务代码中,经常会使用到 ThreadLocal 用于跨业务代码 来获取在上游设置的值。比如,在spring mvc中 spring web mvc 中通过 RequestContextHolder 设置 HttpServletRequest,业务代码则可以在 controller 或者是 service中 通过 RequestContextHolder#getRequestAttributes 获取相应的对象. 但这种方法有一个限制,即 setValue 和 getValue 的代码必须在同一个线程内. 当然,这也是属于通过 ThreadLocal 来避免竞争的一种手法.
针对之前已经可以工作的代码,如果将相应的业务代码 迁移至一个新的线程池中运行,即封装为 1个 runnable 对象,那么相应的代码即不能正确地工作了。
如下参考所示
ThreadLocal<String> local = new ThreadLocal<>(); local.set("value1"); //打印 获取->value1 System.out.println("获取->" + local.get()); Runnable runnable = () -> { System.out.println("获取->" + local.get()); }; //打印 获取->null new Thread(runnable).start();
上面的 sout 可以是任意1个业务上的 method 调用。仅仅是将 method调用 转由新线程来运行,相应的业务逻辑即不能正常工作。 至于这里将调用转由新线程来运行,可以有很多的场景。如 支持 timedout 调用(利用future.get(timed))。
本文通过反射调用读取当前Thread的信息,将值注入到新线程中的threadLocal中,以达到透传threadLocal的目的。不需要修改任何业务代码,也不需要使用InheritableThreadLocal(此类也并不用于当前场景)
主要的实现基于以下步骤
- 提前提取当前线程ThreadLocal变量值
- 执行时复制至新线程中
- 执行结束之后删除未变化值
本文中的代码均基于反射调用,需要打开相应的 Accessible 属性.
继续阅读“一种在线程池中透传或继承ThreadLocal信息的方法”