每日一學--ThreadLocal,學--ThreadLocal

來源:互聯網
上載者:User

每日一學--ThreadLocal,學--ThreadLocal

ThreadLocal翻譯成中文比較準確的叫法應該是:線程局部變數。

  這個玩意有什麼用處,或者說為什麼要有這麼一個東東?先解釋一下,在並發編程的時候,成員變數如果不做任何處理其實是線程不安全的,各個線程都在操作同一個變數,顯然是不行的,並且我們也知道volatile這個關鍵字也是不能保證安全執行緒的。那麼在有一種情況之下,我們需要滿足這樣一個條件:變數是同一個,但是每個線程都使用同一個初始值,也就是使用同一個變數的一個新的副本。這種情況之下ThreadLocal就非常使用,比如說DAO的資料庫連接,我們知道DAO是單例的,那麼他的屬性Connection就不是一個安全執行緒的變數。而我們每個線程都需要使用他,並且各自使用各自的。這種情況,ThreadLocal就比較好的解決了這個問題。

  我們從源碼的角度來分析這個問題。

  首先定義一個ThreadLocal:

public class ConnectionUtil {    private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();    private static Connection initConn = null;    static {        try {            initConn = DriverManager.getConnection("url, name and password");        } catch (SQLException e) {            e.printStackTrace();        }    }        public Connection getConn() {        Connection c = tl.get();        if(null == c) tl.set(initConn);        return tl.get();    }}

 

package java.lang;import java.lang.ref.*;import java.util.concurrent.atomic.AtomicInteger;public class ThreadLocal<T> {       private final int threadLocalHashCode = nextHashCode();       private static AtomicInteger nextHashCode =        new AtomicInteger();    private static final int HASH_INCREMENT = 0x61c88647;    private static int nextHashCode() {        return nextHashCode.getAndAdd(HASH_INCREMENT);    }    protected T initialValue() {        return null;    }    public ThreadLocal() {    }    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();    }    private T setInitialValue() {        T value = initialValue();        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else            createMap(t, value);        return value;    }    public void set(T value) {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else            createMap(t, value);    }     public void remove() {         ThreadLocalMap m = getMap(Thread.currentThread());         if (m != null)             m.remove(this);     }    ThreadLocalMap getMap(Thread t) {        return t.threadLocals;    }    void createMap(Thread t, T firstValue) {        t.threadLocals = new ThreadLocalMap(this, firstValue);    }    static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {        return new ThreadLocalMap(parentMap);    }    T childValue(T parentValue) {        throw new UnsupportedOperationException();    }    static class ThreadLocalMap {        static class Entry extends WeakReference<ThreadLocal> {            /** The value associated with this ThreadLocal. */            Object value;            Entry(ThreadLocal k, Object v) {                super(k);                value = v;            }        }        private static final int INITIAL_CAPACITY = 16;        private Entry[] table;        private int size = 0;        private int threshold; // Default to 0        private void setThreshold(int len) {            threshold = len * 2 / 3;        }        private static int nextIndex(int i, int len) {            return ((i + 1 < len) ? i + 1 : 0);        }        private static int prevIndex(int i, int len) {            return ((i - 1 >= 0) ? i - 1 : len - 1);        }        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 ThreadLocalMap(ThreadLocalMap parentMap) {            Entry[] parentTable = parentMap.table;            int len = parentTable.length;            setThreshold(len);            table = new Entry[len];            for (int j = 0; j < len; j++) {                Entry e = parentTable[j];                if (e != null) {                    ThreadLocal key = e.get();                    if (key != null) {                        Object value = key.childValue(e.value);                        Entry c = new Entry(key, value);                        int h = key.threadLocalHashCode & (len - 1);                        while (table[h] != null)                            h = nextIndex(h, len);                        table[h] = c;                        size++;                    }                }            }        }        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;        }        private void set(ThreadLocal key, Object value) {            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 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) {                    e.clear();                    expungeStaleEntry(i);                    return;                }            }        }        private void replaceStaleEntry(ThreadLocal key, Object value,                                       int staleSlot) {            Entry[] tab = table;            int len = tab.length;            Entry e;            int slotToExpunge = staleSlot;            for (int i = prevIndex(staleSlot, len);                 (e = tab[i]) != null;                 i = prevIndex(i, len))                if (e.get() == null)                    slotToExpunge = i;            for (int i = nextIndex(staleSlot, len);                 (e = tab[i]) != null;                 i = nextIndex(i, len)) {                ThreadLocal k = e.get();                if (k == key) {                    e.value = value;                    tab[i] = tab[staleSlot];                    tab[staleSlot] = e;                    if (slotToExpunge == staleSlot)                        slotToExpunge = i;                    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);        }        private int expungeStaleEntry(int staleSlot) {            Entry[] tab = table;            int len = tab.length;            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();                if (k == null) {                    e.value = null;                    tab[i] = null;                    size--;                } else {                    int h = k.threadLocalHashCode & (len - 1);                    if (h != i) {                        tab[i] = null;                        while (tab[h] != null)                            h = nextIndex(h, len);                        tab[h] = e;                    }                }            }            return i;        }        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;        }        private void rehash() {            expungeStaleEntries();            // Use lower threshold for doubling to avoid hysteresis            if (size >= threshold - threshold / 4)                resize();        }        private void resize() {            Entry[] oldTab = table;            int oldLen = oldTab.length;            int newLen = oldLen * 2;            Entry[] newTab = new Entry[newLen];            int count = 0;            for (int j = 0; j < oldLen; ++j) {                Entry e = oldTab[j];                if (e != null) {                    ThreadLocal k = e.get();                    if (k == null) {                        e.value = null; // Help the GC                    } else {                        int h = k.threadLocalHashCode & (newLen - 1);                        while (newTab[h] != null)                            h = nextIndex(h, newLen);                        newTab[h] = e;                        count++;                    }                }            }            setThreshold(newLen);            size = count;            table = newTab;        }        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);            }        }    }}

這樣子,都是用同一個串連,但是每個串連都是新的,是同一個串連的副本。

  那麼實現機制是如何的呢?

1、每個Thread對象內部都維護了一個ThreadLocalMap這樣一個ThreadLocal的Map,可以存放若干個ThreadLocal

/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ThreadLocal.ThreadLocalMap threadLocals = null;

2、當我們在調用get()方法的時候,先擷取當前線程,然後擷取到當前線程的ThreadLocalMap對象,如果非空,那麼取出ThreadLocal的value,否則進行初始化,初始化就是將initialValue的值set到ThreadLocal中。

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

 

3、當我們調用set()方法的時候,很常規,就是將值設定進ThreadLocal中。

4、總結:當我們調用get方法的時候,其實每個當前線程中都有一個ThreadLocal。每次擷取或者設定都是對該ThreadLocal進行的操作,是與其他線程分開的。

5、應用情境:當很多線程需要多次使用同一個對象,並且需要該對象具有相同初始化值的時候最適合使用ThreadLocal。

6、其實說再多也不如看一下源碼來得清晰。如果要看源碼,其中涉及到一個WeakReference和一個Map,這兩個地方需要瞭解下,這兩個東西分別是a.Java的弱引用,也就是GC的時候會銷毀該引用所包裹(引用)的對象,這個threadLocal作為key可能被銷毀,但是只要我們定義成他的類不卸載,tl這個強引用就始終引用著這個ThreadLocal的,永遠不會被gc掉。b.和HashMap差不多。

  事實上,從本質來講,就是每個線程都維護了一個map,而這個map的key就是threadLocal,而值就是我們set的那個值,每次線程在get的時候,都從自己的變數中取值,既然從自己的變數中取值,那肯定就不存線上程安全問題,總體來講,ThreadLocal這個變數的狀態根本沒有發生變化,他僅僅是充當一個key的角色,另外提供給每一個線程一個初始值。如果允許的話,我們自己就能實現一個這樣的功能,只不過恰好JDK就已經幫我們做了這個事情。

轉載自:http://www.cnblogs.com/dreamroute/p/5034726.html

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.