为什么要使用ThreadLocal
简单理解,是为了减少参数的传递:有些参数,是同一个线程内多个类的多个方法都要使用的,一种办法是直接在各方法之间传递这些参数,另一种就是使用ThreadLocal。
例如,用户登录后,这个用户信息(用session代表)可能在很多个地方、多个方法中调用。一种办法是直接把用户信息作为参数在方法之间传递,但使用ThreadLocal更简洁。
这篇文章举的例子:
不使用ThreadLocal前,传递session:
SessionHandler handler = new SessionHandler();
Session session = handler.createSession();
handler.getStatus(session);
handler.getUser(session);
handler.setStatus(session, "close");
handler.getStatus(session);
使用ThreadLocal之后:
SessionHandler handler = new SessionHandler();
handler.getStatus();
handler.getUser();
handler.setStatus("close");
handler.getStatus();
但如果让我选,我可能还是会选择第一种,直接传递参数的方式,它虽然笨,但不容易出错(更坏的情况是,有可能接手你代码的哥们并不了解ThreadLocal......)。ThreadLocal还可能带来内存泄露的问题,例子在这里
使用ThreadLocal的例子还有:
多数据源切换
Spring事务中的ThreadLocal
使用ThreadLocal存储用户登录信息
使用ThreadLocal实现用户信息随用随取
ThreadLocal的实现原理
详细见这篇文章
核心的原理就是,每个线程都有一个map:
private static final ThreadLocal myThreadLocal = new ThreadLocal();
myThreadLocal.set(myValue);
上面的代码,我们拿到的有3个对象:
Thread.currentThread
myThreadLocal
myValue
拿到了Thread.currentThread,就拿到了它的map,然后往这个map里放,key就是myThreadLocal ,value就是myValue。
那为什么要用map呢?
那是因为,一个线程可能有多个ThreadLocal(定义多个类,在多个类中定义static final的ThreadLocal实例),也就是你可以定义多个ThreadLocal,然后都绑定到同一个线程上。
ThreadLocal的几个重要的认识
1.ThreadLocal并不是解决变量共享的问题
因为它根本就没有共享变量,每个线程是有自己的变量。
例如下面的
hibernate session管理
每次调用sessionFactory.openSession都会创建一个新的session(而不是copy,后面会说到这个copy的问题),然后绑定在调用的线程上,后续同一个线程,使用的就是同一个session。Spring的事务也是基于类似的原理。
源码(注意sessionFactory.openSession()):
public class DAO {
private static final ThreadLocal session = new ThreadLocal();
private static final SEssionFactory sessionFactory =
new Configuration().configure().buildSessionFactory();
public static Session getSession() {
Session session = (Session) DAO.session.get();
if (session == null) {
session = sessionFactory.openSession();
DAO.session.set(session);
}
return session;
}
public static void close() {
getSession().close();
DAO.session.set(null);
}
}
但是, ThreadLocal实例是共享的,多个线程之间,ThreadLocal是同一个
什么意思呢?
private static final ThreadLocal myThreadLocal= new ThreadLocal();
上面代码的myThreadLocal是static final,只创建一次,它在不同的线程中,是作为key的,所以它在多个线程中是同一个。那它会有线程不安全的问题吗?没有。因为它只是作为getValue的key,只读不写。而且要注意的是,myThreadLocal本身并不存储value,value是存储在线程的map字段的。这一点非常重要,要看源码才能明白。
2.ThreadLocal没有copy变量
官方文档的copy我理解是一个虚指,不是实指。个人推测,它说的copy可能是指,代码里是只定义了一个session变量,但实际上每个线程都会有一个session,所以看起来是把session“复制”(copy)了一份。事实上,每个session都是不同的对象,都是sessionFactory新创建的。Spring管理数据库connection的例子中,每次取connection然后调用ThreadLocal.set(connection)时,也是从数据库连接池中取的不同的connection。
This class provides thread-local variables.These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable.
网友评论