ThreadLocal 为每个使用变得的线程保存一个副本,这样每个线程操作的就是自己的副本. 简述: 每个Thread 都有一个ThreadLocalMap的变量, 当在线程中调用ThreadLocal.set()的时候初始化。 ThreadLocalMap存储键值对 key = ThreadLacal value = “用户设置的变量” 。 这里的 key 是一个WeakRefrences意味着 key 可能被 GC 回收。 对应的get() 从 Thread.ThreadLocalMap 从取。这里可能存在几种情况 没有值,key 被 GC 回收了 有值。具体看下面的代码逻辑

 /**
    取到 Thread  的 threadLocals 如果 = null 初始化 将 value put 进去 ,
    已经初始化过了 ,直接 put
  **/
 public void set(T value) {
        Thread t = Thread.currentThread();
            // Thread.threadLocals
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);  (1else
            // 初始化 Thead.threadLocals 这里逻辑很简单 
            createMap(t, value);  (2)
    }

//——————————————————-
//ThreadLocalmMap.set
private void set(ThreadLocal<?> key, Object value) {

        <span class="hljs-comment">// We don't use a fast path as with get() because it is at</span>
        <span class="hljs-comment">// least as common to use set() to create new entries as</span>
        <span class="hljs-comment">// it is to replace existing ones, in which case, a fast</span>
        <span class="hljs-comment">// path would fail more often than not.</span>

        Entry[] tab = table;
        <span class="hljs-keyword">int</span> len = tab.length;
        <span class="hljs-comment">// 散列取得key映射到数据的位置 i 这里 相当于 </span>
        <span class="hljs-comment">// key.threadLocalHashCode % (len) 但用位运算效率更高</span>
        <span class="hljs-keyword">int</span> i = key.threadLocalHashCode &amp; (len-<span class="hljs-number">1</span>);
        <span class="hljs-comment">// 当前i位置不为空 说明 hash 碰撞了 向后线性探索 遇到e = null 停止     </span>
        <span class="hljs-keyword">for</span> (Entry e = tab[i];
             e != <span class="hljs-keyword">null</span>;
             e = tab[i = nextIndex(i, len)]) {
            ThreadLocal&lt;?&gt; k = e.get();
            <span class="hljs-keyword">if</span> (k == key) {
                <span class="hljs-comment">//找到匹配的key  </span>
                e.value = value;
                <span class="hljs-keyword">return</span>;
            }
            <span class="hljs-keyword">if</span> (k == <span class="hljs-keyword">null</span>) {
                 <span class="hljs-comment">// 这key = null key别GC回收了 </span>
                replaceStaleEntry(key, value, i);
                <span class="hljs-keyword">return</span>;
            }
        }
        <span class="hljs-comment">// 位置上没有Entry </span>
        tab[i] = <span class="hljs-keyword">new</span> Entry(key, value);
        <span class="hljs-keyword">int</span> sz = ++size;
        <span class="hljs-keyword">if</span> (!cleanSomeSlots(i, sz) &amp;&amp; sz &gt;= threshold)
            rehash();
    }
复制代码

一个简单的图 说明set过程

set

 private void replaceStaleEntry(ThreadLocal<?> key, Object value,
                                       int staleSlot) {
            Entry[] tab = table;
            int len = tab.length;
            Entry e;
        <span class="hljs-comment">//从 staleSlot 位置 向前搜索stale 节点位置  (连续空间因为这里遇到e = null 会停止 )</span>
        <span class="hljs-keyword">int</span> slotToExpunge = staleSlot;
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = prevIndex(staleSlot, len);
             (e = tab[i]) != <span class="hljs-keyword">null</span>;
             i = prevIndex(i, len))
            <span class="hljs-keyword">if</span> (e.get() == <span class="hljs-keyword">null</span>)
                slotToExpunge = i;

         <span class="hljs-comment">// 向后搜索 </span>
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = nextIndex(staleSlot, len);
             (e = tab[i]) != <span class="hljs-keyword">null</span>;
             i = nextIndex(i, len)) {
            ThreadLocal&lt;?&gt; k = e.get();

            <span class="hljs-comment">// If we find key, then we need to swap it</span>
            <span class="hljs-comment">// with the stale entry to maintain hash table order.</span>
            <span class="hljs-comment">// The newly stale slot, or any other stale slot</span>
            <span class="hljs-comment">// encountered above it, can then be sent to expungeStaleEntry</span>
            <span class="hljs-comment">// to remove or rehash all of the other entries in run.</span>
            <span class="hljs-comment">// 找到对应的key,与 stale entity 交换</span>
            <span class="hljs-keyword">if</span> (k == key) {
                e.value = value;

                tab[i] = tab[staleSlot];
                tab[staleSlot] = e;

                <span class="hljs-comment">// Start expunge at preceding stale entry if it exists</span>
                <span class="hljs-keyword">if</span> (slotToExpunge == staleSlot)
                    slotToExpunge = i;
                cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
                <span class="hljs-keyword">return</span>;
            }

            <span class="hljs-comment">// If we didn't find stale entry on backward scan, the</span>
            <span class="hljs-comment">// first stale entry seen while scanning for key is the</span>
            <span class="hljs-comment">// first still present in the run.</span>
            <span class="hljs-keyword">if</span> (k == <span class="hljs-keyword">null</span> &amp;&amp; slotToExpunge == staleSlot)
                slotToExpunge = i;
        }

        <span class="hljs-comment">// If key not found, put new entry in stale slot</span>
        tab[staleSlot].value = <span class="hljs-keyword">null</span>;
        tab[staleSlot] = <span class="hljs-keyword">new</span> Entry(key, value);

        <span class="hljs-comment">// If there are any other stale entries in run, expunge them</span>
        <span class="hljs-comment">// 清除别的 stale entity</span>
        <span class="hljs-keyword">if</span> (slotToExpunge != staleSlot)
            cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
    }
复制代码

说明下replaceStatleEntity的流程

repalceStatleEntiry

  private boolean cleanSomeSlots(int i, int n) {
            boolean removed = false;
            Entry[] tab = table;
            int len = tab.length;
            do {
                // i 用永远不能是一个无效的位置
                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;
        }

//———————————————————————–
private int expungeStaleEntry(int staleSlot) {
Entry[] tab = table;
int len = tab.length;

        <span class="hljs-comment">// expunge entry at staleSlot </span>
        <span class="hljs-comment">// 擦除staleSlot 位置的entry </span>
        tab[staleSlot].value = <span class="hljs-keyword">null</span>;
        tab[staleSlot] = <span class="hljs-keyword">null</span>;
        size--;

        <span class="hljs-comment">// Rehash until we encounter null</span>
        Entry e;
        <span class="hljs-keyword">int</span> i;
        <span class="hljs-keyword">for</span> (i = nextIndex(staleSlot, len);
             (e = tab[i]) != <span class="hljs-keyword">null</span>;
             i = nextIndex(i, len)) {
            ThreadLocal&lt;?&gt; k = e.get();
            <span class="hljs-comment">// 从staleSlot 位置向后搜索 </span>
            <span class="hljs-comment">// key = null 擦除</span>
            <span class="hljs-comment">// key != null rehash 如果位置变了  向后搜索找到一个空位置放</span>
            <span class="hljs-keyword">if</span> (k == <span class="hljs-keyword">null</span>) {
                e.value = <span class="hljs-keyword">null</span>;
                tab[i] = <span class="hljs-keyword">null</span>;
                size--;
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-keyword">int</span> h = k.threadLocalHashCode &amp; (len - <span class="hljs-number">1</span>);
                <span class="hljs-keyword">if</span> (h != i) {
                    tab[i] = <span class="hljs-keyword">null</span>;

                    <span class="hljs-comment">// Unlike Knuth 6.4 Algorithm R, we must scan until</span>
                    <span class="hljs-comment">// null because multiple entries could have been stale.</span>
                    <span class="hljs-keyword">while</span> (tab[h] != <span class="hljs-keyword">null</span>)
                        h = nextIndex(h, len);
                    tab[h] = e;
                }
            }
        }
        <span class="hljs-keyword">return</span> i;
    }
复制代码

remove

private void remove(ThreadLocal<?> key) {
            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)]) {
                if (e.get() == key) {
                    // 擦除 key 相同的位置   
                    e.clear();
                    expungeStaleEntry(i);
                    return;
                }
            }
        }
复制代码

rehash

  private void rehash() {
            expungeStaleEntries();
        <span class="hljs-comment">// Use lower threshold for doubling to avoid hysteresis</span>
        <span class="hljs-keyword">if</span> (size &gt;= threshold - threshold / <span class="hljs-number">4</span>)
            resize();
    }

/**
从头到尾 清除一下无效 Entity
**/

private void expungeStaleEntries() {
Entry[] tab = table;
int len = tab.length;
for (int j = 0; j < len; j++) {
Entry e = tab[j];
if (e != null && e.get() == null)
expungeStaleEntry(j);
}
}

private void resize() {
Entry[] oldTab = table;
int oldLen = oldTab.length;
int newLen = oldLen * 2;
Entry[] newTab = new Entry[newLen];
int count = 0;

        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> j = <span class="hljs-number">0</span>; j &lt; oldLen; ++j) {
            Entry e = oldTab[j];
            <span class="hljs-keyword">if</span> (e != <span class="hljs-keyword">null</span>) {
                ThreadLocal&lt;?&gt; k = e.get();
                <span class="hljs-keyword">if</span> (k == <span class="hljs-keyword">null</span>) {
                    e.value = <span class="hljs-keyword">null</span>; <span class="hljs-comment">// Help the GC</span>
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-comment">// 重新hash 找到扩容后的table 位置 </span>
                    <span class="hljs-keyword">int</span> h = k.threadLocalHashCode &amp; (newLen - <span class="hljs-number">1</span>);
                    <span class="hljs-keyword">while</span> (newTab[h] != <span class="hljs-keyword">null</span>)
                        h = nextIndex(h, newLen);
                    newTab[h] = e;
                    count++;
                }
            }
        }

        setThreshold(newLen);
        size = count;
        table = newTab;
    }
复制代码

总结: 代码大概都能看懂,但是设计中为什么这么设计,为什么选择了这样的算法, 我也不懂,可能是基础太差

  • Android

    开放手机联盟(一个由 30 多家科技公司和手机公司组成的团体)已开发出 Android,Android 是第一个完整、开放、免费的手机平台。

    293 引用
感谢    赞同    分享    收藏    关注    反对    举报    ...