ThreadLocal分析

大家推荐个靠谱的公众号程序员探索之路,大家一起加油,这个公众号已经接入图灵https://i-blog.csdnimg.cn/blog_migrate/93320939ba8f8b0a898e29429753f496.png ​ 

1.ThreadLocal 作用: 每个线程从ThreadLocal的实例对象中取出的对象 都是自己线程拥有的

2.同步和ThreadLocal都是解决多线程中数据访问问题的两种思路
同步是数据共享的思路   ThreadLocal是数据隔离的思路
同步是时间换空间    ThreadLocal是空间换时间

3.注意 ThreadLocal的值是存在Thread实例的ThreadLocal.ThreadLocalMap threadLocals中
TheadLocal Thread  ThreadLocalMap之间的关系
TheadLocal 可以看作是一个工具 真正存储数据的是 ThreadLocalMap ThreadLocalMap中的元素的key是ThreadLocal对象
每个Thead实例都有一个ThreadLocalMap实例

源码分析:
4.set方法
/**
        1.获取当前线程
    2.获取该线程的ThreadLocalMap  --- 注意虽然名字叫map 但是 不是咱们理解的map 里面是ThreadLocalMap.Entry[] 每个元素是以key-value的形式存在的
    3.1 放入值
    3.2 创建map,放入值
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

/**
    初始大小是16
*/
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }

/**
设置扩充数据的门栏   = 长度 * 2/3  这个和判断数组是否需要扩容有关
*/
private void setThreshold(int len) {
            threshold = len * 2 / 3;
        }

/**
放入值方法 大致思路是这样的  
1.根据threadLocalHashCode&长度获取位置
2.从该位置开始 判断 当前位置的Entry是null 直接new Entry出来 
3.不为null 判断key是否一样的 一样把value覆盖
4.不一样 且这个位置的Entry 的key(也就是ThreadLocal对象)为null 替换当前位置的Entry
为什么会有null的情况 static class Entry extends WeakReference<ThreadLocal<?>> 可以看到 Entry是弱引用 可能被垃圾回收了 
为什么用弱引用 解释是避免内存占用过多
5.如果key不一样而且也没有垃圾回收掉 那么只好换一个地方了 
怎么换tab[i = nextIndex(i, len)] 取上面计算的位置的下一个位置  如果超过了长度从0开始 然后重复2-4判断
*/

    private void set(ThreadLocal<?> key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            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)]) {
                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;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
/**
ThreadLocal的threadLocalHashCode是怎么来的  第一次是0 以后每一次加HASH_INCREMENT  
为什么加这个  目的是散列更均匀  为什么要很均匀上面的set会告诉我们
*/

private final int threadLocalHashCode = nextHashCode();

    /**
     * The next hash code to be given out. Updated atomically. Starts at
     * zero.
     */
    private static AtomicInteger nextHashCode =
        new AtomicInteger();

    /**
     * The difference between successively generated hash codes - turns
     * implicit sequential thread-local IDs into near-optimally spread
     * multiplicative hash values for power-of-two-sized tables.
     */
    private static final int HASH_INCREMENT = 0x61c88647;

    /**
     * Returns the next hash code.
     */
    private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }

来测试一下 长度为16和32 来获取的值
16: 0 7 14 5 12 3 10 1 8 15  32: 0 7 14 21 28 3 10 17 24 31

5.获取方法
/**
1.获取当前线程的ThreadLocalMap
2.根据 ThreadLoca实例获取key
3.如果获取不到 那么 就返回一个默认的值(默认值是null) 然后和上面的set方法都同一个流程 只是value是null
*/
    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();
    }

/**
1.计算ThreadLocal在map中的位置 
2.如果该位置不为空 返回这个Entry
3.如果为空 那么走getEntryAfterMiss方法
*/
    private Entry getEntry(ThreadLocal<?> key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }

/**
其实这个获取Entry方法的逻辑和set方法获取Entry位置的逻辑是一样的
*/
     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;
        }
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值