阅读-ThreadLocal(JAVA)

ThreadLocal其实就是一个工具类,用来操作线程局部变量。
每个线程在jvm的虚拟机里都分配有自己独立的空间,线程之间对于本地的空间是相互隔离的。那么ThreadLocal就应该是该线程空间里本地可以访问的数据了。
首先ThreadLocal有一个静态内部类ThraedLocalMap
这个Map为每个线程复制一个变量的‘拷贝’存储其中。

static class ThreadLocalMap {
    //是一个Entry类型的数组
    private Entry[] table;
    //初始table容量大小为16
    private static final int INITIAL_CAPACITY = 16;
    //数组元素的个数
    private int size = 0;

    private int threshold; // Default to 0 阈值
    //这个类继承WeakReference 说明这个类是一个弱引用
    static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

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

补充(上面所说的弱引用):
在java中,存在四种引用:强引用、软引用、弱引用、虚引用。
强引用(Strong Reference):通常我们通过new(堆上)来创建一个新对象时返回的引用就是一个强引用,若一个对象通过一系列强引用可到达,它就是强可达的(strongly reachable),那么它就不被回收。
软引用和弱引用(Soft Reference):软引用和弱引用的区别在于,若一个对象是弱引用可达,无论当前内存是否充足它都会被回收,而软引用可达的对象在内存不充足时才会被回收,因此软引用要比弱引用“强”一些。
也就是如果Map中如果是map<Product,Integer>如果将productA赋值为null,然而map还引用这productA,那么productA过去指向的对象并不会被回收,如果改为弱引用则可以立即被回收掉。
虚引用(Phantom Reference):虚引用是Java中最弱的引用,那么它弱到什么程度呢?它是如此脆弱以至于我们通过虚引用甚至无法获取到被引用的对象,虚引用存在的唯一作用就是当它指向的对象被回收后,虚引用本身会被加入到引用队列(ReferenceQueue)中,用作记录它指向的对象已被回收。

先来看ThreadMap中的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;//get长度
            int i = key.threadLocalHashCode & (len-1); //通过此线程的HashCode找到对应的table下标(存储位置)

            //如果i位置已经存储了对象,那么就往后挪一个位置依次类推,直到找到空的位置,再将对象存放。
            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();//接下来查看
        }

rehash方法

 private void rehash() {
             //执行清理工作 ,由于Entry是弱引用,查找key为null的进行GC(遍历Key)
            expungeStaleEntries();

            // Use lower threshold for doubling to avoid hysteresis
            //如果超出了重新扩充并将对象重新计算位置。
            if (size >= threshold - threshold / 4)
                resize();
        }

(2)key.threadLocalHashCode & (len-1)
ThreadLocalMap中Entry[ ]table的大小必须是2的N次方(len = 2^N)
能使哈希码分配均匀

>>> HASH_INCREMENT = 0x61c88647
>>> def magic_hash(n):
...     for i in range(n):
...         nextHashCode = i * HASH_INCREMENT + HASH_INCREMENT
...         print nextHashCode & (n - 1),
...     print
... 
>>> magic_hash(16)
7 14 5 12 3 10 1 8 15 6 13 4 11 2 9 0
>>> magic_hash(32)
7 14 21 28 3 10 17 24 31 6 13 20 27 2 9 16 23 30 5 12 19 26 1 8 15 22 29 4 11 18 25 0

对于ThreadLocal

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
public T get() {
        //获取当前线程
        Thread t = Thread.currentThread();
        //获取当前thread的TheadLocalMap
        ThreadLocalMap map = getMap(t);
        //如果map不为空
        if (map != null) {

            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

通过当前线程,来获得这个线程中的ThreadLocalMap,然后得到实例,
那么对于set,同样,获取当前线程,获得这个线程的ThreadLocalMap,然后,调用这个map的set方法(得到map中的table表,获取长度,得到一个随机分布的索引值,然后要么放入,要么更新值)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值