ThreadLocal在框架中的运用
ThreadLocal在JAVA框架中得到了大量的运用,甚至上升为一种设计模式,这里随便举几个例子:
- Struts2中Action的管理
- Spring中数据库模板Connection的管理
- Hibernate中Session的管理
- MyBatis中SQLSession的管理
具体代码可以通过查看源码,或者搜索引擎查找,此处不再赘述。
ThreadLocal在实战中的运用
ThreadLocal在笔者当前的项目中,用于封装MyBatis的分页操作,基本流程就是拦截需要分页的请求,初始化ThreadLocal容器,然后在分页插件中将页数信息存储到容器中,返回结果后再清理容器。
不过在实践中,发现了一个有趣的问题:
SpringMVC 3.2以后添加了异步请求功能,在异步Controller中使用分页上下文时,将无法获取ThreadLocal容器;原因很简单,主线程A进来后,被拦截并初始化ThreadLocal容器,然后异步Controller将会开启多个子线程;接下来在分页拦截器中,想要获取ThreadLocal,将无法获取,因为子线程并不享有父线程的ThreadLocal变量,因而就只能得到空指针异常了。
这个问题的解决方法也挺简单的,使用InheritableThreadLocal来替换ThreadLocal即可。
InheritableThreadLocal
“InheritableThreadLocal”这个单词其实已经在上篇文章 中出现过了,就是Thread源码里的inheritableThreadLocals变量。
相对于ThreadLocal,InheritableThreadLocal的线程安全性稍差。创建一个线程时如果保存了所有 InheritableThreadLocal 对象的值,那么这些值也将自动传递给子线程。如果一个子线程调用 InheritableThreadLocal 的 get() ,那么它将与它的父线程看到同一个对象。为保护线程安全性,应该只对不可变对象(一旦创建,其状态就永远不会被改变的对象)使用 InheritableThreadLocal ,因为对象被多个线程共享。 InheritableThreadLocal 很合适用于把数据从父线程传到子线程,例如用户标识(user id)或事务标识(transaction id),但不能是有状态对象,例如 JDBC Connection 。