持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情
3.1 线程隔离
在2.4ThreadLocal的使用示例中,我们发现一个很有趣的事情,ThreadLocal在不同的线程,好像能够存储不同的数据:就好像ThreadLocal本身就有存储功能,到了不同线程,能够生成不同的“副本”存储数据一样。
实际上,ThreadLocal到底是怎么做到的呢?
看下set方法,看看到底怎么存数据的,这就用到2.3章节提到的ThreadLocalMap。
ThreadLocal类
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value){
Thread t = Thread.currentThread();
ThreadLocal.ThreadLocalMap map = getMap(t);
if(map != null){
map.set(this,value);
}else {
createMap(t,value);
}
}
//获取当前Thread的threadLocals变量
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocal.ThreadLocalMap getMap(Thread t){
return t.threadLocals;
}
其实这地方做了一个很有意思的操作:线程数据隔离的操作,是Thread类和ThreadLocal类相互配合做到的
public class Thread implements Runnable{
...
/*ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class.
**/
ThreadLocal.ThreadLocalMap threadLocals = null;
...
}
在上边的代码中可以看出来,在set数据的时候,会获取这行该操作的当前线程
Thread t = Thread.currentThread();
拿到当前线程,取到threadLocals变量,然后以当前实例为key,数据为value存入ThreadLocalMap中。
所以使用ThreadLocal在不同的线程中进行写操作,实际上数据都是绑定在当前线程的实例上,ThreadLocal只负责读写操作,并不负责保存数据,这就解释了,为什么ThreadLocal set数据,只在操作的线程中用。
大家有没有感觉这种思路有些巧妙!
3.2 ThreadLocalMap
另外我们可以从源码中看到每一个Thread维护一个ThreadLocalMap,ThreadLocalMap中key为使用弱引用的ThreadLocal实例,value为线程变量的副本。一定程度上减少了内存泄露的风险,但如果使用不当也会引起内存泄露。
* ThreadLocalMap is a customized hash map suitable only for
* maintaining thread local values. No operations are exported
* outside of the ThreadLocal class. The class is package private to
* allow declaration of fields in class Thread. To help deal with
* very large and long-lived usages, the hash table entries use
* WeakReferences for keys. However, since reference queues are not
* used, stale entries are guaranteed to be removed only when
* the table starts running out of space.
*/
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
...
}
3.3与Synchronized比较
使用领域不同:
Synchronized是用于多线程下相同资源并发访问的一种有效方式;
ThreadLocal是隔离多个线程的数据共享。
使用ThreadLocal存储数据其实是将数据存在了线程的私有内存里面,就不会存在线程安全问题、多线程下资源访问问题。
四 注意事项
1.使用完ThreadLocal变量后,确定不再需要用到该变量,调用其remove方法,避免内存泄漏;
2.一个ThreadLocal实例,在一个线程中只能储存一类数据,后期的set操作,会覆盖之前的set的数据;
3.ThreadLocal变量通常被定义为private static 。
{@code ThreadLocal} instances are typically private
static fields in classes that wish to associate state with a thread(e.g., a user ID or Transaction ID).
这样做的目的是希望与当前线程的状态相关联,一直存在ThreadLocal的强引用,也就能保证任何时候都能通过ThreadLocal的弱引用访问到Entry的value值,进而清除掉 。
五 总结
ThreadLocal对象本身是不储存数据的,它本身的职能是执行相关的set、get之类的操作,如果需要隔离多个线程之间的共享冲突,可以考虑使用ThreadLocal,能简化我们的程序。