ThreadLocal源码分析

1.对ThreadLocal的理解

ThreadLocal是一个创建线程局部量的类。使用ThreadLocal创建的变量只能被当前线程访问,其他线程则无法访问和修改。

2.主要方法的分析

2.1 set()

//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 getMap(Thread t) {
        return t.threadLocals;
    }

set的过程可以分为3个过程:
1.获取当前的线程
2.获取线程里面的ThreadLocal.ThreadLocalMap
3.看这个ThreadLocal.ThreadLocalMap是否存在,存在就设置一个值,不存在就给线程创建一个ThreadLocal.ThreaLocalMap
第三点有两个分支,如下:

void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
//
ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
//
private final int threadLocalHashCode = nextHashCode();
//
private static int nextHashCode() {
    return nextHashCode.getAndAdd(HASH_INCREMENT); 
    }
//
private static AtomicInteger nextHashCode = 
    new AtomicInteger();

这个Map中并没有next节点,所以他的存储方式不是链表的形式。
下面看一下其创建分支:

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();
        }
private static int nextIndex(int i, int len) {
            return ((i + 1 < len) ? i + 1 : 0);
        }

上面代码的大致逻辑可以分为:
1.先对ThreadLocal里面的threadLocalHashCode取模获取到一个table的位置。
2.如果这个位置有数据,获取这个位置上的ThreaLocal
(1)判断一下位置上的ThreadLocal和本身这个是不是一个ThreadLocal,是的话数据就覆盖。
(2)如果不是同一个ThreadLocal,就判断一下这个位置上的Threadlocal是否为空,返回。

2.2 get()

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }
//获取Entry
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);
        }
//获取失败后的处理
private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) {
            Entry[] tab = table;
            int len = tab.length;

            while (e != null) {
                ThreadLocal k = e.get();
                if (k == key)
                    return e;
                if (k == null)
                    expungeStaleEntry(i);
                else
                    i = nextIndex(i, len);
                e = tab[i];
            }
            return null;
        }

概括一下上面的逻辑:
1.获取当前线程
2.去当前线程中得到ThreadLocal.ThreadLocalMap
3.当前线程中判断是否有ThreadLocal.ThreadLocalMap
(1)有就尝试根据当前ThreadLocal的ThreadLocalHashCode取模取table的值,有就返回,没有就+1继续找。
(2)没有就调用set方法给当前线程ThreadLocal.ThreadLocalMap设置一个初始值。

2.3remove()

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

取得当前线程的ThreadLocal.ThreadLocalMap,如果有ThreadLocal.ThreadLocalMap,找到对应的Entry,移除就可以了。

参考:http://www.cnblogs.com/xrq730/p/4854813.html

你可能感兴趣的:(ThreadLocal源码分析)