1. 概述
ThreadLocal为每个使用该变量的线程提供独立的变量副本,因此每一个线程都可以独立地改变自己的副本,而不会影响其他线程。
入门例子:
public class ThreadLocalStudy {
static ThreadLocal<String> stringThreadLocal = new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
new Thread(
() -> {
stringThreadLocal.set("5");
System.out.println(stringThreadLocal.get());
test();
}
).start();
TimeUnit.SECONDS.sleep(10);
}
public static void test() {
System.out.println(stringThreadLocal.get());
}
}
2. 源码阅读
ThreadLocal.ThreadLocalMap threadLocals 是 Thread局部属性字段
public class Thread implements Runnable {
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}
ThreadLocal.ThreadLocalMap
key: ThreadLocal 弱引用 value: 对应的值
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
private static final int INITIAL_CAPACITY = 16;
private Entry[] table;
private int size = 0;
private int threshold; // Default to 0
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}
private static int prevIndex(int i, int len) {
return ((i - 1 >= 0) ? i - 1 : len - 1);
}
ThreadLocal 常用方法源码阅读:
public void set(T value) {
Thread t = Thread.currentThread();
// 1. 获取当前线程的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null)
// 2.1 设置值 key:stringThreadLocal value: 对应的值
map.set(this, value);
else
// 2.2 创建Map
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
// ThreadLocalMap key: stringThreadLocal value: 对应的值
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
public T get() {
Thread t = Thread.currentThread();
// 1. 获取当前线程的threadLocals
ThreadLocalMap map = getMap(t);
if (map != null) {
// 2. 使用key stringThreadLocal 获取 Entry
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
// 返回结果
return result;
}
}
// threadLocals 设置value为null的键值对,并返回null
return setInitialValue();
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
// 移除当前线程的 threadLocals中 stringThreadLocal 的键值对
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
}
3. ThreadLocalMap的key为什么用弱引用
当 线程中的 threadLocal 被GC回收,threadLocal在堆中只有弱引用指向,就可以被回收了,但是value不会被回收,所以还是会有内存泄漏的情况,所以代码运行完成要用 remove清除一下。
测试代码如下:
public class ThreadLocalStudy {
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i <= 100; i++) {
System.out.println(i);
TimeUnit.SECONDS.sleep(1);
new Thread(
() -> {
ThreadLocal<byte[]> threadLocal = new ThreadLocal<>();
byte[] oneM = new byte[1024 * 1024];
threadLocal.set(oneM);
System.out.println(threadLocal.get().length);
// 切断threadLocal的强引用
threadLocal = null;
System.gc();
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
).start();
}
TimeUnit.MINUTES.sleep(100);
}
}
堆大小 20m
运行截图: