ThreadLocal源码学习整理

ThreadLocal源码学习分析

变量

斐波那契散列乘数

​ 0x61c88647 是斐波那契散列乘数,他的优点是通过它散列出来的结果会分布比较均匀,可以很大程度避免hash冲突。

// 定义初始化变量值 原子更新该值 保证每个ThreadLocal对象该属性均不相同
private final int threadLocalHashCode = nextHashCode();

private static AtomicInteger nextHashCode =
    new AtomicInteger();


private static final int HASH_INCREMENT = 0x61c88647;

// 每次初始化 threadLocalHashCode属性时候,都会更新该值 保证每个ThreadLocal对象该属性均不相同
private static int nextHashCode() {
    return nextHashCode.getAndAdd(HASH_INCREMENT);
}

构造方法
public ThreadLocal() {
}

方法set()

// 向当前线程的ThreadLocalMap中设置值
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);  // 1
    if (map != null)
        // 如果当前的map不为空,将ThreadLocal对象作为键  设置的变量作为值设置
        map.set(this, value); 
    else
        // map为空  初始化调用线程的ThreadLocalMap
        createMap(t, value); // 2
}

// 获取调用线程的ThreadLocalMap属性值
ThreadLocalMap getMap(Thread t) {   // 1
    return t.threadLocals;
}

ThreadLocal.ThreadLocalMap threadLocals = null;// 1
    
// 将该线程的threadLocals属性初始化
void createMap(Thread t, T firstValue) {  // 2
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}
    
	
  	private static final int INITIAL_CAPACITY = 16;

    private Entry[] table;

    private int size = 0;

    private int threshold; // Default to 0

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { // 2
    // 初始化Entry数组为默认16
    table = new Entry[INITIAL_CAPACITY];
    // 该ThreadLocal对象的threadLocalHashCode 与 15进行按位与操作,获取该key存放位置 初始为7
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    // 将值存放在位置处
    table[i] = new Entry(firstKey, firstValue);
    // 设置该Map的size是1
    size = 1;
    // 设置达到总容量的2/3时扩容
    setThreshold(INITIAL_CAPACITY);
}
    
方法get()
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        // 获取该ThreadLocal对应的Entry
        ThreadLocalMap.Entry e = map.getEntry(this); // 1
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}


private Entry getEntry(ThreadLocal<?> key) {   // 1
    // 使用该ThreadLocal的hashcode和现在table的length - 1进行按位与 (为什么不是之前的初始容量)
    int i = key.threadLocalHashCode & (table.length - 1);

    // 定位到相应Entry
    Entry e = table[i];
    // 如果存在entry 返回
    if (e != null && e.get() == key)
        return e;
    else
        // 不存在entry
        return getEntryAfterMiss(key, i, e);
}


private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {   // 1
    Entry[] tab = table;
    int len = tab.length;
	
    // 如果当前Entry有值 倒是 key不是该ThreadLocal的话
    while (e != null) {
        // 获取该ThreadLocal
        ThreadLocal<?> k = e.get();
        // 如果为该ThreadLocal 返回Entry
        if (k == key)
            return e;
        // 如果key为空 说明 因为key是弱引用 垃圾回收直接就回收掉了 造成空值 对其value进行处理
        if (k == null)
            expungeStaleEntry(i); // 1
        else
            i = nextIndex(i, len); // 2
        // 如果e最后为null的时候  退出循环
        e = tab[i];
    }
    return null;
}

// 处理下标为i处的值
private int expungeStaleEntry(int staleSlot) {  // 1
    Entry[] tab = table;
    int len = tab.length;

    // 把该下标置位null size -1
    tab[staleSlot].value = null;
    tab[staleSlot] = null;
    size--;

    // 将所有的null值为键的元素处理了
    Entry e;
    int i;
    for (i = nextIndex(staleSlot, len);
         (e = tab[i]) != null;
         i = nextIndex(i, len)) {
        ThreadLocal<?> k = e.get();
        if (k == null) {
            e.value = null;
            tab[i] = null;
            size--;
        } else {
            int h = k.threadLocalHashCode & (len - 1);
            if (h != i) {
                tab[i] = null;

                while (tab[h] != null)
                    h = nextIndex(h, len);
                tab[h] = e;
            }
        }
    }
    return i;
}

// 1 + i超没超数组长度  没超就继续往下遍历数组 直到找到相应的key, 否则返回0
private static int nextIndex(int i, int len) { // 2
    return ((i + 1 < len) ? i + 1 : 0);
}
remove方法
public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        m.remove(this);
}

 private void remove(ThreadLocal<?> key) {
     Entry[] tab = table;
     int len = tab.length;
     int i = key.threadLocalHashCode & (len-1);
     for (Entry e = tab[i];
          e != null;
          e = tab[i = nextIndex(i, len)]) {
         if (e.get() == key) {
             e.clear();
             expungeStaleEntry(i);
             return;
         }
     }
 }
ThreadLocalMap中的Entry
// WeakReference 弱引用 
static class Entry extends WeakReference<ThreadLocal<?>> {

    // 强引用
    Object value;
	
    // key是弱引用 只能存活到下一次GC前
    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}
引用的区别

强、软、弱、虚

强引用:一直存活,除非GC Roots不可达

软引用:内存不足的时候回被回收

弱引用:只能存活到下一次GC前

虚引用:随时会被回收,创建了可能很快就会被回收

小结

​ 每个线程都有自己独立的ThreadLocalMap对象,ThreadLocalMap对象中存放的是Entry数组。

Entry中的键是弱引用,值是强引用,所以有可能造成GC时将键回收,可能会造成内存泄漏。

​ Entry中存放的键 是ThreadLocal对象, 值是set进的值。

​ 对于ThreadLocal对象来说,他的索引值是确定了,即在不同的线程访问时候的访问的不同的table

数组的同一个位置都是 table[i], 只不过这些个table是独立的。

​ 同一个线程的不同ThreadLocal对象,共享一个ThreadLocalMap中的Entry数组table,每个对象为一个键值对。对象之间在数组中有不同的下标。

ThreadLocal和Synchronized的区别

​ 前者是牺牲空间来保证变量冲突,后者是牺牲时间保证对变量操作的原子性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ThreadLocal源码Java中一个关键的类,它提供了一种在多线程环境下实现线程本地变量的机制。在JDK 8之前和之后,ThreadLocal的内部结构有所变化。ThreadLocal源码分为两部分:ThreadLocal类和ThreadLocalMap类。 ThreadLocal类是一个泛型类,它包含了两个核心方法:set()和get()。set()方法用于将一个值与当前线程关联起来,get()方法用于获取当前线程关联的值。 ThreadLocalMap类是ThreadLocal的内部类,它用于存储每个线程的本地变量。在JDK 8之前,ThreadLocalMap是通过线性探测法解决哈希冲突的,每个ThreadLocal对象都对应一个Entry对象,Entry对象包含了ThreadLocal对象和与之关联的值[2]。 在JDK 8之后,ThreadLocalMap的实现方式发生了改变。使用了类似于HashMap的方式,采用了分段锁的机制来提高并发性能。每个线程维护一个ThreadLocalMap对象,其中的Entry对象也是采用链表的形式来解决哈希冲突。 总结起来,ThreadLocal源码主要由ThreadLocal类和ThreadLocalMap类组成。ThreadLocal类提供了set()和get()方法来管理线程本地变量,而ThreadLocalMap类则负责存储每个线程的本地变量,并解决哈希冲突的问题。 史上最全ThreadLocal 详解 ThreadLocal源码分析_02 内核(ThreadLocalMap) 【JDK源码】线程系列之ThreadLocal 深挖ThreadLocal ThreadLocal原理及内存泄露预防 ThreadLocal原理详解——终于弄明白了ThreadLocal ThreadLocal使用与原理 史上最全ThreadLocal 详解。 ThreadLocal源码分析,主要有ThreadLocal源码以及ThreadLocal的内部结构在jdk8前后的变化。 使用方式非常简单,核心就两个方法set/get public class TestThreadLocal { private static final ThreadLocal<String> threadLocal = new ThreadLocal<>(); public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { try { threadLocal.set("aaa"); Thread.sleep(500); System.out.println("threadA:" threadLocal.get()); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { @Override public void run() { threadLocal.set("bbb"); System.out.println("threadB:" threadLocal.get()); } }).start(); } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值