ThreadLocal的工作原理

ThreadLocal是什么

  • ThreadLocal是一个能够隔离线程的数据存储类。特定线程存储的数据只有该特定线程才能够获取到。

ThreadLocal的原理

低版本中

ThreadLocal中最重要的就是set和get方法,我们一起来看一下

set

	public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }

看一下values和initializeValues方法

	Values values(Thread current) {
        return current.localValues;
    }
    ...
    Values initializeValues(Thread current) {
        return current.localValues = new Values();
    }

localValues引用是关键

	/**
     * Normal thread local values.
     */
    ThreadLocal.Values localValues;

我们可以发现,每个线程里面都含有一个ThreadLocal.Values的引用,这个就是ThreadLocal能够隔离线程的最重要的原因

我们接着看value是怎么存储的

	void put(ThreadLocal<?> key, Object value) {
            cleanUp();

            // Keep track of first tombstone. That's where we want to go back
            // and add an entry if necessary.
            int firstTombstone = -1;

            for (int index = key.hash & mask;; index = next(index)) {
                Object k = table[index];

                if (k == key.reference) {
                    // Replace existing entry.
                    table[index + 1] = value;
                    return;
                }

                if (k == null) {
                    if (firstTombstone == -1) {
                        // Fill in null slot.
                        table[index] = key.reference;
                        table[index + 1] = value;
                        size++;
                        return;
                    }

                    // Go back and replace first tombstone.
                    table[firstTombstone] = key.reference;
                    table[firstTombstone + 1] = value;
                    tombstones--;
                    size++;
                    return;
                }

                // Remember first tombstone.
                if (firstTombstone == -1 && k == TOMBSTONE) {
                    firstTombstone = index;
                }
            }
        }

可以看到,每个线程有一个ThreadLocal.Values的引用,而ThreadLocal.Values这个类里面有个

		/**
         * Map entries. Contains alternating keys (ThreadLocal) and values.
         * The length is always a power of 2.
         */
        private Object[] table;
  • 而ThreadLocal和ThreadLocal包含的value就是通过key.reference放在index,而value放在index+1的位置来识别的。而存放位置则是使用了类似HashMap的存储原理。

get

	public T get() {
        // Optimized for the fast path.
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values != null) {
            Object[] table = values.table;
            int index = hash & values.mask;
            if (this.reference == table[index]) {
                return (T) table[index + 1];
            }
        } else {
            values = initializeValues(currentThread);
        }

        return (T) values.getAfterMiss(this);
    }

可以看到,这里的操作很简单,就是使用了类似HashMap的原理,通过hash值来计算下标,进而获取value

  • 这里有一个需要注意的点是,这里的哈希冲突的解决是通过存储的时候通过特殊方式使生成的hash不会冲突了来解决的。

这样,ThreadLocal的原理就讲解完成了,但是ThreadLocal在高版本发生了许多变化,我们一起来看一下

高版本

set和get

	public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    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();
    }
  • 可以看到,这里有一个比较大的区别就是使用了ThreadLocalMap代替了Values

我们一起看看这两者有什么区别

ThreadLocalMap替换了Values

重点看一下set方法

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

可以发现这里主要有两个区别,

  • 一个是Object[]被替换成了Entry[]
  • 而是这里的存储彻底使用了HashMap的存储原理

高版本的变化是为了什么?

  • 在我看来,主要是为了更好的解决Hash冲突,低版本中,每个ThreadLocal需要占用table的两个字段,而高版本中只需要占用一个,这个改变极大的减低了Hash冲突带来的效率降低。

以上内容属于笔者看源码的所得,可能有些地方不一定完全正确,欢迎大家指导交流

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值