1、ThreadLocal简介
java.lang.ThreadLocal是线程本地变量,意为每个线程私有的变量,不同线程间不共享。
它的意义在于线程间的数据隔离。
ThreadLocal有个内部类ThreadLocalMap,key就是当前线程,value就是线程正在执行的任务中的某个变量的包装类Entry。
2、ThreadLocal使用姿势
ThreadLocal变量需要定义为static,原因是非static变量的实例会有多个,也就是会有多个ThreadLocal实例,这是没必要的,只是增加了内存消耗。而static ThreadLocal 的ThreadLocalMap在每个线程都有一个副本。
3、ThreadLocal应用
常见的ThreadLocal使用场景为数据库连接、Session管理等。
数据库连接管理类,原本可以用同步或锁保证线程安全,但这会影响效率。这个场景的特点是,每个线程都有connect请求,但各自的连接之间是没有依赖的关系的,也就是每个线程各自管理好各自的小线程池就够了,那么这正好是ThreadLocal擅长做的事。代码参考如下(来自参考资料):
private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {
public Connection initialValue() {
return DriverManager.getConnection(DB_URL);
}
};
public static Connection getConnection() {
return connectionHolder.get();
}
4、ThreadLocal的坑
由于ThreadLoacal是和线程绑定的,也就是说ThreadLocal变量的生命周期是和线程一样的,理论上线程结束后ThreadLocal就回收了。可是在线程池中,对线程的管理是线程会复用,线程往往不会注销,这意味着ThreadLocal的生命周期和JVM一致。这会导致什么问题呢?
假设ThreadLocal中存了一个HashMap,如果业务上HashMap的key不断变化,那么HashMap会不断变长。由于对HashMap数据的写入,一般会先校验这个HashMap是否已经存在,若不存在,则创建一个,若存在,一般为避免未知的业务异常也不会替换掉。这样的HashMap实例越来越多,会逐渐形成内存泄露而无法管理。
线程池使用ThreadLocal时,如果没有及时remove,而下一个线程没有调用set方法,那么get到的就会是错误信息。
参考资料:
https://www.iteye.com/topic/103804 这篇文章写的非常不错,推荐
http://www.cnblogs.com/yxysuanfa/p/7125761.html ThreadLocal的坑