ThreadLocal实现原理

6 篇文章 0 订阅
3 篇文章 0 订阅

ThreadLocal简介

原文地址
ThreadLocal是java中将非线程安全变为线程安全的一个神器.通过为每一个线程保存一个线程本地变量副本来保证数据的安全性,通过set()和get()方法来使用. 因为每个线程都拥有变量的副本.不和其他线程变量交互,所以不会出现线程安全问题.所以这里有一个点就是ThreadLocal应用的场景应该是变量的访问是没有依赖关系的,每个线程只和自己的数据副本打交道.

属性

public class ThreadLocal<T> {
    //每个线程有自己的HashCode,并不是根据线程计算的,而是Local分配的
    private final int threadLocalHashCode = nextHashCode();

    //用来计算下一个线程的hash值,threadLocalHashCode就是根据这个Integer计算出来的,每次增加一个一个HASH_INCREMENT的值
    private static AtomicInteger nextHashCode =
        new AtomicInteger();
    //hash增量,hash值的增量
    private static final int HASH_INCREMENT = 0x61c88647;
}

主要方法

get()

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

看代码可以知道首先获取当前线程,然后通过getMap获取到一个ThreadLocalMap.如果当前线程存在map的话,就从map中取出当前线程所需要的值.注意这里getEntry中传入的是this,不是当前线程t.每个线程中的ThreadLocalMap中存储的是键值对是ThreadLocal对象引用与ThreadLocal变量存储的值的对应.
接下来看getMap方法

getMap()

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

这里就是获得线程自己的ThreadLocalMap.每个线程都有一个ThreadLocal变量.在初始的时候是空的.

// 这是线程中的ThreadLocalMap变量
ThreadLocal.ThreadLocalMap threadLocals = null;

然后上面获取到的.如果获取到的map不是空的,说明已经设置过本地变量.因此就直接获取到此map的键值对.然后返回所要的值.
如果map为空,就通过setInitialValue()方法设置初始值.

setInitialValue()

private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

    //这个方法是用来给子类继承的.在ThreadLoacl中SuppliedThreadLocal内部类继承了此类,用来实现这个方法.设置初始值
 protected T initialValue() {
        return null;
    } 

这个代码也很清晰明了了,如果当前线程有本地map,就直接添加默认值,如果没有,就使用createMap()创建一个

createMap()

创建Map其实就是给当前线程的ThreadLocal赋值.

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

接下来细看一下ThreadLocalMap类的内部实现

ThreadLocalMap

在这里map中的Entry存储的key是一个ThreadLocal变量,值就是设置的值.这里有一点要搞明白.每个线程都有一个ThreadLocalMap的变量.通过这个变量能够找到当前线程存储的每个的ThreadLocal所对应的值.

static class ThreadLocalMap {
    //这里在说一下,这里使用的是弱引用.是为了让在线程将此值释放掉后能够让虚拟机进行回收,当线程本地释放变量的时候,不需要在对map进行主动移除,虚拟机就会自动回收掉无用变量.
    static class Entry extends WeakReference<ThreadLocal<?>> {
            Object value;
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
    //省略一些基本的map的操作与字段,里面也是使用数组来存储数据.使用线性探测来解决hash冲突.

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

重点看一下getEntry,通过得到当前线程保存的ThreadLocal变量.然后在通过这个值找到对应表中的位置,在这里有一点可能会出现为空或者这个key值不是当前线程保存的本地ThreadLocal的变量的情况.通过在线性探测法遍历整个数组,如果出现为空的话,则直接返回空.

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

这里set方法首先就获取当前线程,在获取一个ThreadLocalMap(),然后设置值

总结

到这里可能会有一些人对这个ThreadLocal还有点懵逼,通过一个完整的例子来看一下.

    private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();
    private static final ThreadLocal<String> threadLocal1 = new ThreadLocal<>();

比如说这里面有两个ThreadLocal,上面讲过,线程中存储的是一个ThreadLocalMap,map中存储的是ThreadLocal变量与值的映射.比如这里,线程中的threadLocalMap中会有两对键值对,一个是threadLocal的,另一个是threadLocal1的.当然,拥有这两个键值对的前提是调用了threadLocal.set()方法或者是get方法.如果调用了threadLocal.set(str)方法,那么就会在此线程的map中添加一个映射,就是threadLocal->str的映射.如果在获取的时候,通过获取线程的threadLocalMap,找到此threadLocal对应的值,就是此线程保存的这个threadLocal的值了.

ThreadLocal的实现原理主要就是通过每个线程保存一个ThreadLocalMap的变量,在ThreadLocal中保存的ThreadLoca => value 的键值对映射的一个map,通过找到线程对应的threadlocalmap,然后在从threadlocalmap中找到对应的threadlocal对应的值,就能够获取到对应的变量副本.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值