【ThreadLocal 你想要的都在这里了】系统理解TreadLocal的相关知识点,一遍就会。

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()方法进行清理‌。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值