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

运行截图:

相关推荐
Alive~o.01 分钟前
Go语言进阶&依赖管理
开发语言·后端·golang
花海少爷3 分钟前
第十章 JavaScript的应用课后习题
开发语言·javascript·ecmascript
手握风云-4 分钟前
数据结构(Java版)第二期:包装类和泛型
java·开发语言·数据结构
喵叔哟24 分钟前
重构代码中引入外部方法和引入本地扩展的区别
java·开发语言·重构
尘浮生30 分钟前
Java项目实战II基于微信小程序的电影院买票选座系统(开发文档+数据库+源码)
java·开发语言·数据库·微信小程序·小程序·maven·intellij-idea
hopetomorrow44 分钟前
学习路之PHP--使用GROUP BY 发生错误 SELECT list is not in GROUP BY clause .......... 解决
开发语言·学习·php
不是二师兄的八戒1 小时前
本地 PHP 和 Java 开发环境 Docker 化与配置开机自启
java·docker·php
小牛itbull1 小时前
ReactPress vs VuePress vs WordPress
开发语言·javascript·reactpress
请叫我欧皇i1 小时前
html本地离线引入vant和vue2(详细步骤)
开发语言·前端·javascript
闲暇部落1 小时前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin