ThreadLocal
应用场景
主要用于做数据隔离,保证同一个变量在不同线程下操作时互不干扰,可以理解为每个线程单独复制一份变量保存在线程中,对保存的副本就行操作。
一个是用于上下文通信,因为ThreadLocal使用后如果步手动移除,value会一直保存(可能会发生内存泄漏),所以当一条线程一直存在的时候,可以利用这个特性对上下文都用到的对象进行存储,不用频繁的传参数。
第二个是多线程的安全问题,如SimpleDateFormat,我们使用SimpleDateFormat的parse()方法,内部有一个Calendar对象,调用SimpleDateFormat的parse()方法会先调用Calendar.clear(),然后调用Calendar.add(),如果一个线程先调用了add()然后另一个线程又调用了clear(),这时候parse()方法解析的时间就不对了。这个时候要保证各线程之间的操作互不干扰,可以使用ThreadLocal对各个线程进行隔离操作。
如何实现?
ThreadLocal其实并不是一个实例,可以理解为一个代理公司,实际操作的是Thread中的ThreadLocals对象,而用户创建的每一个ThreadLocal都会对应一个hashCode值,作为key存储在ThreadLocals中,当某个线程调用某个ThreadLocal的get、set方法时,首先获得当前线程的ThreadLocals,然后再根据使用的ThreadLocal获得HashCode值,找到对应的key-value,进行操作。
ThreadLocals
ThreadLocals是一个实例化的ThreadLocalMap对象,ThreadLocalMap是ThreadLocal的静态内部类,在某个Thread中初始化为null,
//Thread类中代码片段
ThreadLocal.ThreadLocalMap threadLocals = null;
//ThreadLocal类中代码片段
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
ThreadLocalMap
key属于弱引用,容易被GC,value属于强引用,所以容易出现value一直存在,导致内存泄漏,所以应该用完就remove
关系
大概就是这么个关系,Thread中存储有实例化的ThreadLocalMap对象,变量名叫ThreadLocals,其中存有键值对,key是ThreadLocal的HashCode,value是ThreadLocal+其set值,当使用某个ThreadLocal的时候,先获得当前调用ThreadLocal的线程,然后再根据ThreadLocal的HashCode找到其对应的map,然后对Map中的value进行操作。但是要注意内存泄漏,使用结束后即使remove
InheritableThreadLocal
使用InheritableThreadLocal可以实现多个线程访问ThreadLocal的值,我们在主线程中创建一个InheritableThreadLocal的实例,然后在子线程中得到这个InheritableThreadLocal实例设置的值。
private void test() {
final ThreadLocal threadLocal = new InheritableThreadLocal();
threadLocal.set("帅得一匹");
Thread t = new Thread() {
@Override
public void run() {
super.run();
Log.i( "张三帅么 =" + threadLocal.get());
}
};
t.start();
}
如果线程的inheritThreadLocals变量不为空,比如我们上面的例子,而且父线程的inheritThreadLocals也存在,那么我就把父线程的inheritThreadLocals给当前线程的inheritThreadLocals。
参考文章
感谢大佬
如果有错误,还请大佬指出。非常感谢。