ThreadLocal源码解析

4 篇文章 0 订阅
3 篇文章 0 订阅

ThreadLocal是什么

threadLocal是一个线程内部的存储类,可以在线程执行中存储数据,通常情况下我们创建的变量每一个线程都可以访问,而ThreadLocal则实现了线程的隔离性,保证了存储的数据只有当前线程可以访问得到

ThreadLocal使用场景

在Java的多线程编程中,为保证多个线程对共享变量的安全访问,通常会使用synchronized来保证同一时刻只有一个线程对共享变量进行操作。这种情况下可以将类变量放到ThreadLocal类型的对象中,使变量在每个线程中都有独立拷贝,不会出现一个线程读取变量时而被另一个线程修改的现象。最常见的ThreadLocal使用场景为用来解决数据库连接、Session管理等。

ThreadLocal的核心原理

ThreadLocal是基于ThreadLocalMap来实现的线程隔离,ThreadLocalMap是一个类似于HashMap的一个K,V键值对的数据结构,内部的实现也与HashMap基本上一模一样,只不过Entry内部类有所变化,我们先来看看ThreadLocalMap中的Entry类

        static class Entry extends WeakReference<ThreadLocal<?>> {
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

ThreadLocalMap中的Entry的Key也就是ThreadLocal是弱引用,这里简单解释一下弱引用,在java中有引用的概念,即某个对象需要为一个变量所引用才能被触达,而java中提供了四种引用,强引用,软引用,弱引用以及虚引用,平时我们正常的使用的都是强引用,哪怕内存不足抛出OOM异常也不会回收被强引用的对象,软引用则是当JVM即将OOM的时候才会回收,弱引用是当JVM进行GC时扫描到就会被回收,虚引用则是任何时候都有可能被回收.

这里解释一下为什么key采用弱引用,首先ThreadLocalMap只有当前线程可以访问,而如果当前线程访问结束而被销毁,但是当前线程在ThreadLocalMap中的存储的value还在,并且永远也访问不到,如果当前key是强引用,而该值在系统中永远无法被访问到,那么这就引起了内存泄漏,所以java开发团队考虑到这种情况,于是将Entry中的key设为弱引用,以防止内存泄漏的问题,而在调用set,get等其他方法时 会清理key为null的方法

ThreadLocal提供了一些api来提供我们使用

  • get()
  • set(T val)
  • remove()

通过api的名称不难看出他们的功能,set是插入数据,get是获取数据,remove是删除数据,我们先来康康set方法

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

private void set(ThreadLocal<?> key, Object value) {
            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();
        }

通过这里可以看出ThreadLocalMap其实是Thread中的一个变量,每一个Thread持有一个ThreadLocalMap,而ThreadLocalMap中的每一个key则是对应的ThreadLocal,而其他的方法基本与HashMap中的逻辑差不多,而多加了一步cleanSomeSlots,就是清理呗回收了的key为null的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();
}

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

上图的table是Object[],其实就是个map,这里的map用的是线性探测解决冲突,而hashmap是用拉链法。

调用getEntryAfterMiss线性探测,过程中每碰到无效slot,调用expungeStaleEntry进行段清理;如果找到了key,则返回结果entry,所以这里get也有去除一些无效引用的作用,上面的while语句就是个线性探测。

再看一下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;
                }
            }
        }

remove方法相对于getEntry和set方法比较简单,直接在table中找key,如果找到了,把弱引用断了做一次段清理。

至此源码部分就介绍完了,我们简单回顾一下ThreadLocal

首先ThreadLocal是基于静态内部类ThreadLocalMap,ThreadLocalMap是一个<K,V>形式的存储数据的结构,ThreadLocalMap中的key是ThreadLocal的弱引用变量,而ThreadLocalMap是存储在Thread中的一个变量,ThreadLocal利用当前获取当前线程的ThreadLocalMap进行get,set以及remove方法

我的公众号

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值