背景介绍
ThreadLocal可以为每个使用该变量的线程都保存一个独立的副本
Demo演示
public class ThreadLocalDemo {
public static void main(String[] args) throws InterruptedException {
final ThreadLocal<String> req = new ThreadLocal<String>();
new Thread() {
@Override
public void run() {
req.set("123");
System.out.println("thread1:" + req.get());// thread1:123
}
}.start();
new Thread() {
@Override
public void run() {
req.set("456");
System.out.println("thread2:" + req.get());// thread2:456
}
}.start();
Thread.sleep(1000);
}
}
应用场景
- ThreadLocal 用作保存每个线程独享的对象,为每个线程都创建一个副本,这样每个线程都可以修改自己所拥有的副本, 而不会影响其他线程的副本,确保了线程安全
ThreadLocal 用作每个线程内需要独立保存信息,以便供其他方法更方便地获取该信息的场景。每个线程获取到的信息可能都是不一样的,前面执行的方法保存了信息后,后续方法可以通过ThreadLocal 直接获取到,避免了传参,类似于全局变量的概念
原理分析
调用流程
在调用ThreadLocal的set方法时,Threadlocal会先获取当前线程,通过当前线程获取其中的成员变量(ThreadLocalMap类型,Thread类中的内部类)threadLocals,这个变量与map类似,也是通过key -> value的形式存储数据。最后ThreadLocalMap将当前ThreadLocal对象作为key,将set的值作为value进行存储
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; } }初始化数组长度
初始化长度为16
/** * The initial capacity -- MUST be a power of two. */ private static final int INITIAL_CAPACITY = 16;扩容阈值
当达到2/3时扩容
/** * Set the resize threshold to maintain at worst a 2/3 load factor. */ private void setThreshold(int len) { threshold = len * 2 / 3; }hash冲突解决方案
HashMap中当发生hash冲突的时候是采用链表或红黑树解决,但是ThreadLocalMap是通过线性探测法解决。就是当发现数组中该下标元素有数据时,就寻找数组的下一个位置看是否有值
/** * Construct a new map including all Inheritable ThreadLocals * from given parent map. Called only by createInheritedMap. * * @param parentMap the map associated with parent thread. */ private ThreadLocalMap(ThreadLocalMap parentMap) { Entry[] parentTable = parentMap.table; int len = parentTable.length; setThreshold(len); table = new Entry[len]; for (int j = 0; j < len; j++) { Entry e = parentTable[j]; if (e != null) { @SuppressWarnings("unchecked") ThreadLocal<Object> key = (ThreadLocal<Object>) e.get(); if (key != null) { Object value = key.childValue(e.value); Entry c = new Entry(key, value); int h = key.threadLocalHashCode & (len - 1); while (table[h] != null) h = nextIndex(h, len); table[h] = c; size++; } } } }内存泄露问题
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时,可能会把key进行回收,而此时value还在(内存泄露)。JDK团队在set、get和remove时会对其中的key为null的数据进行扫描和清理
