以线程为作用域,每个线程可以定义线程内独有的变量,不会对其它线程或者作用域造成影响。
如某线程调用了如下方法:
void doWork(User user){
functionA(user);
functionB(user);
functionC(user);
functionD(user);
}
线程中频繁地参数传递或者状态传递比较容易出现过度传参、状态不一致等问题。
在上面的doWork方法中,假如ABCD又需要一个新的参数,修改起来也很不方便。
这个时候就可以使用到ThreadLocal来在单个线程作用域内设置和保存一个变量的值或者保持一个状态。
使用ThreadLocal后上面的代码就可以改造成
static final ThreadLocal<User> sThreadLocal = new ThreadLocal<User>();
void doWork(User user){
try{
sThreadLocal.set(user);
functionA();
functionB();
functionC();
functionD();
}...
finally{
//在确认某个变量在线程中不再使用时,需要remove防止内存泄漏
sThreadLocal.remove();
}
}
functionA(){
User user = sThreadLocal.get();
//do something
//...
}
functionB(){...}
......
spring的事务隔离级别控制,如何保证事务之间的独立性单独提交?这里就用到了ThreadLocal用来保证每个事务使用的是同一个数据库连接
TransactionSynchronizationManager类部分源码:
private static final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class);
private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources");
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal<>("Transaction synchronizations");
private static final ThreadLocal<String> currentTransactionName = new NamedThreadLocal<>("Current transaction name");
此外还有session的数据隔离也用到了ThreadLocal。
ThreadLocal是以ThreadLocalMap来存储对象的,每个线程都有一个自己的ThreadLocalMap。取数据时,调用threadLocal的get方法,方法中分三步:
get源码如下:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
set源码如下:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap是Thread的一个属性,被当前线程所引用,所以它的生命周期跟Thread一样长。
ThreadLocalMap是ThreadLocal的内部静态类,当一个线程使用到多个ThreadLocal对象时,对应的ThreadLocalMap则根据多个key去取value。
ThreadLocalMap对Entry的key使用了弱引用,如下:
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
弱引用可以在ThreadLocal使用完成时,由于Entry对ThreadLocal(key)为弱引用,所以ThreadLocal可以被正常回收,但仍有可能存在内存泄漏的问题。
除了threadLocal这个key,还有value是被Entry强引用的,如果entry没有被删除,那么就会造成value的内存泄漏。
这种内存泄漏有两个前提,同时符合这两个前提时,就有可能造成内存泄漏:
由于ThreadLocalMap的生命周期跟Thread一样长,于是导致了该Entry也一直存在于内存中,造成浪费。
因此使用ThreadLocal时,当一个值设置后不再使用,需要手动对其进行删除,调用ThreadLocal的remove方法即可。