你好,我是风一样的树懒,一个工作十多年的后端开发,曾就职京东、阿里等多家互联网头部企业。
由于该模块的内容较多,是面试经常遇到的,而且较长,我们将分成几个模块来学习,也是由于之前最开始的文章太长,大家读起来比较费劲,完读率不是很高,我们进行一定的拆解,慢慢学习。
ThreadLocal 是 Java 提供的一种 线程本地变量存储机制,它允许在同一个线程中的不同方法或类之间共享变量,而不需要显式地传递该变量。每个线程都会拥有自己独立的 ThreadLocal 变量副本,其他线程无法访问。
它主要用于解决多线程环境下变量的共享和隔离,避免了使用 synchronized 进行同步,从而提高了性能。
每个线程内部维护一个 ThreadLocalMap,用于存储 ThreadLocal 变量。它的核心结构如下:
public class ThreadLocal<T> {
// 返回当前线程的ThreadLocalMap,并获取变量值
public T get() { ... }
// 设置当前线程的ThreadLocalMap变量值
public void set(T value) { ... }
// 移除当前线程的ThreadLocal变量,避免内存泄漏
public void remove() { ... }
}
每个 Thread 类都有一个 ThreadLocalMap,其内部维护了 Key-Value 结构:
Key:ThreadLocal 实例(弱引用,防止内存泄漏)
Value:存储的实际值
public class Thread {
// 每个线程都有一个独立的ThreadLocalMap
ThreadLocal.ThreadLocalMap threadLocals = null;
}
ThreadLocalMap 类似于 HashMap,但设计上更简单。它是一个 数组结构,数组中的每个元素都是一个 Entry(类似于 HashMap 的 Node):
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k); // Key是弱引用,防止内存泄漏
value = v;
}
}
private Entry[] table;
}
(3)源码
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
当 ThreadLocal.get() 被调用时:
(1)获取当前线程的 ThreadLocalMap:
ThreadLocalMap map = getMap(Thread.currentThread());
(2)根据 ThreadLocal 的 hashCode() 在 ThreadLocalMap 数组中查找对应的值:
int i = threadLocalHashCode & (table.length - 1);
Entry e = table[i];
(3)如果 Entry 存在,则返回 value:
return e.value;
(4)源码
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
Entry e = map.getEntry(this);
if (e != null) {
return (T) e.value;
}
}
return setInitialValue();
}
当 ThreadLocal.set(value) 被调用时:
(1)获取当前线程的 ThreadLocalMap:
ThreadLocalMap map = getMap(Thread.currentThread());
(2)如果 map 为空,初始化一个 ThreadLocalMap:
if (map == null) {
createMap(Thread.currentThread(), value);
}
(3)计算存储索引,并存储 Entry(ThreadLocal, value):
int i = threadLocalHashCode & (table.length - 1);
table[i] = new Entry(this, value);
remove() 方法用于 防止内存泄漏,当不再需要 ThreadLocal 变量时,应该手动调用 remove():
public void remove() {
ThreadLocalMap map = getMap(Thread.currentThread());
if (map != null) {
map.remove(this);
}
}
今天的内容就分享到这儿,喜欢的朋友可以关注,点赞。有什么不足的地方欢迎留言指出,您的关注是我前进的动力!