ThreadLocal,jdk源码阅读(三)

ThreadLocal的使用

ThreadLocal其实就是给线程设置了一个线程中的局部变量,用来隔离线程,保证数据安全。

public class ThreadLocalDemo {
    static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };
    public static void main(String[] args) {
        Thread[] thread = new Thread[5];
        for (int i = 0 ; i < 5 ; i++){
            thread[i] = new Thread(() -> {
                int num = threadLocal.get();
                threadLocal.set(num+=5);
                System.out.println(Thread.currentThread().getName() + "-" +num);
            });
        }
        for (int i = 0 ;i < 5 ;i++){
            thread[i].start();
        }
    }
}

输出结果
在这里插入图片描述
ThreadLocal的API

  1. T initialValue():该方法用来设置ThreadLocal的初始值,方法在调用get方法的时候会第一次调用,但是如果一开始就调用set方法,则该方法不会被调用。通常该方法只会被调用一次,除非手动调用了remove方法之后又调用get方法,这种情况下,get方法中还是会调用initialValue方法。该方法是protected类型的。很显然是建议在子类重写该方法的,所以通常该方法都会以匿名内部类的形式被方法,以指定初始值。
  2. T get():用来获取与当前线程关联的ThreadLocal的值,如果当前线程没有该ThreadLocal的值,则调用initialValue方法获取初始值返回。
  3. void set(T value):用来设置当前线程的该ThreadLocal的值。
  4. void remove():用来将当前线程的ThreadLocal绑定的值删除,在某些情况下需要手动调用该函数,防止内存泄漏。

ThreadLocal的源码

get()方法

    /**
     * 用来获取与当前线程关联的ThreadLocal的值,如果当前线程没有该ThreadLocal的值,
     * 则调用initialValue方法获取初始值返回。
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
//            获取ThreadLocalMap中的Entry
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
//        如果ThreadLocalMap=null,即第一次调用,则返回initialValue()设置的初始值
        return setInitialValue();
    }
  1. 如果第一次调用get(),则返回initialValue()设置的初始值
private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
//            ThreadLocalMap初始化
            createMap(t, value);
        return value;
    }
//    初始化线程t里面的ThreadLocalMap
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
//        初始化ThreadLocalMap,key是ThreadLocal
        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//            初始化Entry数组,key和value会存入到Entry中
            table = new Entry[INITIAL_CAPACITY];
//            生成数组下标
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
  1. ThreadLocalMap不为null,即不是第一次调用,则根据传入的ThreadLocal拿到数组下标,然后拿到value值
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);
        }

set()方法

//    用来设置当前线程的该ThreadLocal的值。
    public void set(T value) {
        Thread t = Thread.currentThread();
//        每一个线程中都有一个ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
//            第一次的话需要创建一个ThreadLocalMap
            createMap(t, value);
    }

  1. ThreadLocalMap如果为空,需要初始化ThreadLocalMap,和get()方法中的初始化一样
//    初始化线程t里面的ThreadLocalMap
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
//        初始化ThreadLocalMap,key是ThreadLocal
        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//            初始化Entry数组,key和value会存入到Entry中
            table = new Entry[INITIAL_CAPACITY];
//            生成数组下标
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
  1. ThreadLocalMap不为空,则进行赋值
private void set(ThreadLocal<?> key, Object value) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
//            线性探测,解决hash冲突的策略
//            写入:找到发生冲突最近的空闲单元
//            查找:从发生冲突的位置往后查找
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();
//                正常情况,找到Entry然后赋值
                if (k == key) {
                    e.value = value;
                    return;
                }

//                因为Entry是弱引用,所有k值可能为null(内存泄漏问题)
                if (k == null) {
//                    替换脏的Entry
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }
//          此下标i没有Entry
            tab[i] = new Entry(key, value);
            int sz = ++size;
//            扩容
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
  1. 如果Entry中的key被回收,即key==null,需要清除掉脏数据
//        替换脏的Entry
        private void replaceStaleEntry(ThreadLocal<?> key, Object value,
                                       int staleSlot) {
            Entry[] tab = table;
            int len = tab.length;
            Entry e;
            int slotToExpunge = staleSlot;
//           向前查找和向后查找是因为1.如果当前的entry[i]的key=null,附近的节点也可能有脏的Entry
//            2.为了解决hash冲突
//            向前查找遇到脏的Entry,修改slotToExpunge的值,Entry为null时截止
            for (int i = prevIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = prevIndex(i, len))
                if (e.get() == null)
                    slotToExpunge = i;
//            向后查找,Entry为null时截止
            for (int i = nextIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = nextIndex(i, len)) {
                ThreadLocal<?> k = e.get();
//                如果发现后面有k和我们传进来的key相等的Entry,直接替换value
                if (k == key) {
                    e.value = value;
                    
//                    把找到的这个Entry和我们的脏的Entry的位置互换
                    tab[i] = tab[staleSlot];
                    tab[staleSlot] = e;
                    if (slotToExpunge == staleSlot)
                        slotToExpunge = i;
// expungeStaleEntry()定点清除脏数据并继续向下探测找到新的脏数据并清除,以null结束
// cleanSomeSlots()以上一个null为节点继续向下探测,清除所有脏数据
                    cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
                    return;
                }
                if (k == null && slotToExpunge == staleSlot)
                    slotToExpunge = i;
            }
            tab[staleSlot].value = null;
            tab[staleSlot] = new Entry(key, value);
            if (slotToExpunge != staleSlot)
                cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
        }
  1. expungeStaleEntry()定点清除
//        定点清除脏数据并继续向下探测找到新的脏数据并清除,以null结束
        private int expungeStaleEntry(int staleSlot) {
            Entry[] tab = table;
            int len = tab.length;

            // expunge entry at staleSlot
            tab[staleSlot].value = null;
            tab[staleSlot] = null;
            size--;
            Entry e;
            int i;
//            继续往下清除
            for (i = nextIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = nextIndex(i, len)) {
                ThreadLocal<?> k = e.get();
//                k == null直接清除
                if (k == null) {
                    e.value = null;
                    tab[i] = null;
                    size--;
                } else {
//                    h和i不等,清除当前下标i的值,然后把下标h的值找一个为null的地址存放
                    int h = k.threadLocalHashCode & (len - 1);
                    if (h != i) {
                        tab[i] = null;
                        while (tab[h] != null)
                            h = nextIndex(h, len);
                        tab[h] = e;
                    }
                }
            }
//            返回最近的为null的下标
            return i;
        }
  1. cleanSomeSlots()清除所有脏数据
//        以上一个null为节点继续向下探测,清除所有脏数据
        private boolean cleanSomeSlots(int i, int n) {
            boolean removed = false;
            Entry[] tab = table;
            int len = tab.length;
//            继续向下探测
            do {
                i = nextIndex(i, len);
                Entry e = tab[i];
                if (e != null && e.get() == null) {
                    n = len;
                    removed = true;
                    i = expungeStaleEntry(i);
                }
            } while ( (n >>>= 1) != 0);
            return removed;
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值