ThreadLocal核心源码阅读

1. 概述

ThreadLocal为每个使用该变量的线程提供独立的变量副本,因此每一个线程都可以独立地改变自己的副本,而不会影响其他线程。

入门例子:

java 复制代码
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局部属性字段

java 复制代码
public class Thread implements Runnable {
    ThreadLocal.ThreadLocalMap threadLocals = null;
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}

ThreadLocal.ThreadLocalMap

key: ThreadLocal 弱引用 value: 对应的值

java 复制代码
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 常用方法源码阅读:

java 复制代码
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清除一下。

测试代码如下:

java 复制代码
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

运行截图:

相关推荐
皮皮林5515 小时前
IDEA 源码阅读利器,你居然还不会?
java·intellij idea
卡尔特斯10 小时前
Android Kotlin 项目代理配置【详细步骤(可选)】
android·java·kotlin
白鲸开源10 小时前
Ubuntu 22 下 DolphinScheduler 3.x 伪集群部署实录
java·ubuntu·开源
ytadpole10 小时前
Java 25 新特性 更简洁、更高效、更现代
java·后端
纪莫10 小时前
A公司一面:类加载的过程是怎么样的? 双亲委派的优点和缺点? 产生fullGC的情况有哪些? spring的动态代理有哪些?区别是什么? 如何排查CPU使用率过高?
java·java面试⑧股
JavaGuide11 小时前
JDK 25(长期支持版) 发布,新特性解读!
java·后端
用户37215742613511 小时前
Java 轻松批量替换 Word 文档文字内容
java
白鲸开源11 小时前
教你数分钟内创建并运行一个 DolphinScheduler Workflow!
java
Java中文社群12 小时前
有点意思!Java8后最有用新特性排行榜!
java·后端·面试
代码匠心12 小时前
从零开始学Flink:数据源
java·大数据·后端·flink