jdk10 ThreadLocal阅读笔记

java.lang.ThreadLocal,通过每个线程保存单独副本的方式解决并发问题

一般用法
// 定义变量
ThreadLocal<String> tl = new ThreadLocal<String>() {
   @Override
   protected String initialValue() {
       return "init value";
   }
};

// 设置值
tl.set("new value");

// 本线程内取值
String value = tl.get();
相关代码(已上图为例):

  • 构造函数

    生成一个新的hashcode,步长为0x61c88647,增加hashid的间距,减少冲突概率

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

    getMap方法拿到了保存数据的ThreadLocalMap,如果map存在,就以this(ThreadLocal变量)作为key保存数据。看一下getMap的实现:

ThreadLocalMap getMap(Thread t) {
     return t.threadLocals;
}
    从上面代码看出,ThreadLocalMap其实是Thread类的一个属性,每一个ThreadLocal对象在在这个线程里都有一个对应的映射值,因此可以避免 “竟态条件”。
  • 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();
    }
    如图,逻辑是如果能拿到ThreadLocalMap,且存在该entry,就返回entry里面的value;否则,返回initialValue,就是构造函数里重写的那个。

  • remove()
public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        m.remove(this);
}

    从map中移除这个key

看一下这个map的实现(假设已经有hashmap的基础):

  • entry
static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

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

    这样的话,一旦没有其他的强引用,gc的时候会entry会被自动清理掉


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

    用的是开放定址法。从hash开始,向下查找,存在与k(也就是threadlocal对象)相等的weakreference引用则替换,weakreference引用=null则表示该entry中的key(即weakreference中的对象)已经被回收,则替换作废的entry。直到entry为null,表示遇到一个空元素,在table里面新增entry。如果清理失效entry后超出threshold,则执行rehash,类似于hashmap

 

  • get()
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中的对象等于要设置的threadlocal对象。否则,直到遍历到entry=null,表示没有这个key对应的entry


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值