ThreadLocal的set()和get()

什么是ThreadLocal?

通过 ThreadLocal,可以在线程中创建一个变量的副本,每个线程都可以独立地访问和修改自己的副本,而不会影响其他线程的副本。这种机制可以用于避免线程间竞争条件、减少锁的使用,从而提高程序的性能和可维护性
ThreadLocal的基本使用


public static final ThreadLocal<String> LOCAL = new ThreadLocal<>();

public static void main(String[] args) {
    LOCAL.set("hello");
    String result = LOCAL.get();
    System.out.println(result);
}

set()方法逻辑

public class ThreadLocal<T> {
    
  public void set(T value) {
    // 获取当前线程的 ThreadLocalMap
    Thread t = Thread.currentThread();
    //ThreadLocalMap是当前线程Thread类里面的一个属性,是当前线程
    //存储所有ThreadLocal的地方
    ThreadLocalMap map = getMap(t);
    
    // 如果当前线程没有 ThreadLocalMap,创建一个新的并存储
    if (map != null) {
        map.set(this, value);
    } else {
        createMap(t, value);
    }
}
	
   ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
}

ThreadLocalMap的代码写在ThreadLocal类中,但实际的引用是在Thread类中的,ThreadLocal 实际上是通过每个线程独有的 ThreadLocalMap 来实现线程安全的数据隔离的。

public
class Thread implements Runnable{
	ThreadLocal.ThreadLocalMap threadLocals = null;
}

每个线程Thread都有自己的 ThreadLocalMap 实例,ThreadLocalMap 内部有一个哈希表,用于存储每个线程中的线程局部变量。因此每个线程可以独立地存储和访问自己的线程局部变量,不会影响其他线程的数据

public class ThreadLocal<T> {
    
    static class ThreadLocalMap {
    
        private void set(ThreadLocal<?> key, Object value) {
        	//哈希表
            Entry[] tab = table;
            int len = tab.length;
            //计算 ThreadLocal 实例的哈希值,用于定位槽位
            int i = key.threadLocalHashCode & (len-1);

            //如果当前卡槽位不为空,则根据下标i++的方式继续寻找
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();
            	//相同的哈希值,更新并结束
                if (k == key) {
                    e.value = value;
                    return;
                }
            	//当前卡槽的属性为空,则清除过时的哈希表条目
                //并进行必要的重新哈希操作,以保持哈希表的合理性和性能
                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            //如果新增一个Entry,则进行卡槽清理判断
            //如果没有需要清理的卡槽且sz >= 要调整大小的下一个大小值,则进行扩容处理
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

       //数组扩容
       private void rehash() {
           //遍历所有的卡槽,将Entry.get() == null 的卡槽都置为null
           //如果有清空,此时的 size 的值会减少
            expungeStaleEntries();
            // 查看当前的size是否超过阈值
            if (size >= threshold - threshold / 4)
                //如果超过阈值,则进行扩容
                resize();
        }

    	//扩容机制
        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);
                        //如果当前哈希值的卡槽有值了,则newLength+1 继续寻找卡槽
                        //开放定址法
                        while (newTab[h] != null)
                            h = nextIndex(h, newLen);
                        newTab[h] = e;
                        count++;
                    }
                }
            }
        	//设置新的扩容阈值 threshold = len * 2 / 3;
            setThreshold(newLen);
            size = count;
            table = newTab;
        }
    }
}

get()方法逻辑

static class ThreadLocalMap{

    public T get() {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null) {
                //获取卡槽
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null) {
                    @SuppressWarnings("unchecked")  //用来排除警告注解
                    T result = (T)e.value;
                    return result;
                }
            }
            return setInitialValue();
        }

    private Entry getEntry(ThreadLocal<?> key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            //如果当前卡槽不为空,且ThreadLocal的哈希值相同则直接返回
            //否则清除过时的哈希表条目,并进行必要的重新哈希操作
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }

     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;
    }
    
}

清除过时的卡槽并重新哈希操作

 private int expungeStaleEntry(int staleSlot) {
            Entry[] tab = table;
            int len = tab.length;

            // 清除过时的卡槽
            tab[staleSlot].value = null;
            tab[staleSlot] = null;
            size--;

            // 根据当前的下标i++的方式遍历哈希表,直到寻找到的槽位为空为止
            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;

                        // Unlike Knuth 6.4 Algorithm R, we must scan until
                        // null because multiple entries could have been stale.
                        while (tab[h] != null)
                            h = nextIndex(h, len);
                        tab[h] = e;
                    }
                }
            }
            return i;
        }

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值