ThreadLoca是什么?
自己的理解:Thread:线程;Local:本地。ThreadLocal的意思就是线程本地变量,属于各个线程独有的一个变量;
官方的解释:ThreadLocal是一个Java提供的线程本地变量机制,它提供了一种方法,可以创建依赖于当前线程的变量,这些变量的值在不同的线程中是独立的。ThreadLocal类内部使用了一个Map来存储每一个线程的变量副本,从而实现了线程隔离的数据存储。每个线程都可以独立地更改其ThreadLocal变量的值,而不会影响到其他线程的值。这种机制在多线程环境下非常有用,可以避免线程间的数据干扰,保证数据的安全性。
基本的用法:(示例)
public static ThreadLocal<String> TL = new ThreadLocal<>();
public static void main(String[] args) {
new Thread(() -> {
System.out.println(TL.get());
TL.set("i am thread1");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(TL.get());
}).start();
new Thread(() -> {
System.out.println(TL.get());
TL.set("i am thread2");
try {
Thread.sleep(1001);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(TL.get());
}).start();
}
输出的结果:
null
null
i am thread1
i am thread2
ThreadLocal的原理是什么?
自己的理解:利用了ThreadLocalMap这种hash结构,在每次的get/set操作的时候都会默认把当前的线程作为参数带入进去;这样每个线程获取到的都是本线程操作的内容。
源码展示:
//get()函数
public T get() {
Thread t = Thread.currentThread();
ThreadLocal.ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocal.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();
ThreadLocal.ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap的结构是怎样的?
官方解释:ThreadLocalMap是一个基于Entry数组的自定义哈希表,用于存储线程私有的变量值。它通过弱引用和线性探测法等技术手段,实现了线程间的数据隔离和高效的内存管
- 存储结构:
ThreadLocalMap是一个自定义的哈希表,用于存储线程和对应的变量值。它内部以Entry数组的形式存储键值对。
每个Entry对象包含一个ThreadLocal键(实际上是ThreadLocal的弱引用)和对应的变量值。
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new ThreadLocal.ThreadLocalMap.Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new ThreadLocal.ThreadLocalMap.Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
- Entry类:
ThreadLocalMap内部定义了Entry(ThreadLocal<?> k, Object v)节点类,这个节点类继承自WeakReference类,泛型为ThreadLocal类。
Entry的key是ThreadLocal对象的弱引用,value是该ThreadLocal变量在当前线程中的对应的变量实体。
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
- 扩容机制:
ThreadLocalMap的大小可以根据需要进行动态扩容。扩容时,会创建一个新的Entry数组,其长度是原数组的两倍,然后遍历原数组,重新计算每个Entry在新数组中的位置,并存储到新数组中。
扩容的阈值是当前数组长度的2/3 * 3/4 = 1/2,即当元素数量达到数组长度的一半时,会触发扩容。
private void resize() {
Entry[] oldTab = table;
int oldLen = oldTab.length;
int newLen = oldLen * 2;
Entry[] newTab = new Entry[newLen];
int count = 0;
for (int j = 0; j < oldLen; ++j) {
Entry e = oldTab[j];
if (e != null) {
ThreadLocal<?> k = e.get();
if (k == null) {
e.value = null; // Help the GC
} else {
int h = k.threadLocalHashCode & (newLen - 1);
while (newTab[h] != null)
h = nextIndex(h, newLen);
newTab[h] = e;
count++;
}
}
}
setThreshold(newLen);
size = count;
table = newTab;
}
- hash冲突解决:
ThreadLocalMap使用线性探测法来解决哈希冲突。当计算出的哈希位置已被占用时,会依次往后寻找空位置进行存储。
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal<?> k = e.get();
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
- 内存泄漏问题:
由于ThreadLocalMap的key是弱引用,如果ThreadLocal对象没有被外部强引用,那么在垃圾回收时,key可能会被回收,而value则不会被回收,导致内存泄漏。
为了避免内存泄漏,建议在使用完ThreadLocal后,及时调用remove()方法进行清理。