天天看點

高并發---threadlocal

核心思想:

每個線程存有一個map,這個map裡面數組存儲entry,是鍵threadlocal對應的value。意思一個線程對應多個鍵threadlocal對應多個value;(看源碼區分this和thread.currentthread,一個是位元組碼對象一個是目前線程對象)

核心元素:隻有一點

ThreadLocal.ThreadLocalMap

下面三行代碼來自Thread

    ThreadLocal.ThreadLocalMap threadLocals = null;

一、執行個體化,且重寫T initialValue(),初始化首個變量值;     

 java.lang.ThreadLocal<Integer> ad = new java.lang.ThreadLocal<Integer>(){

            public Integer initialValue(){

                return 0;            

            }

        };

那重寫後,哪裡調用?當首次get的時候會調用。

先擷取目前線程,把目前線程裡面的變量ThreadLocalMap 拿出來,

看看是否為空,不為空,就直接擷取this(threadlocal位元組碼對象)對應的值;

空,即把這個Thread裡面的變量ThreadLocalMap初始化, new一個數組(map思想,開放尋址);并把樓上初始化值設定進去(索引就是哈希位與數組長度);

put也就一樣的了;核心就是每個線程Thread裡面的ThreadLocalMap 變量;

    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 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;

        }

擷取過程樓上代碼一層一層跟下去,沒什麼特别。為了解決記憶體溢出問題,jdk大神,把localThread弱引用,原因,如果不是弱引用,就算threadlocal自己null短了引用。但是Thread對threadlocalmap的引用還是在,因為不僅僅你一個threadlocal,接着map裡面還是存在。故弱引用。gc回收了treadlocal。key就變成了null。大神map在put、get等操作如果發現null就把髒資料給remove了。這個是亮點。有時間要研究下。而且你把keynull的去掉。那後面的用不用重新hash呢。因為之前這裡不為空,我把開放尋址的加1放到後面去了。他是不是要挪上來。麻煩

       private int expungeStaleEntry(int staleSlot) {

            Entry[] tab = table;

            int len = tab.length;

            // expunge entry at staleSlot

            tab[staleSlot].value = null;

            tab[staleSlot] = null;

            size--;

            // Rehash until we encounter null

            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;

                        // Unlike Knuth 6.4 Algorithm R, we must scan until

                        // null because multiple entries could have been stale.

                        while (tab[h] != null)

                            h = nextIndex(h, len);

                        tab[h] = e;

                    }

                }

            }

            return i;

        }

執行個體:

多個線程公用一個對象(區域),想擁有同個變量的多個副本。且各個線程一個副本;  

    public static void main(String[] args) {

        test.A a = new test().new A();

        new Thread(new test().new Count("1",a)).start();

        new Thread(new test().new Count("2",a)).start();

    }

public class A{

        java.lang.ThreadLocal<Integer> ad = new java.lang.ThreadLocal<Integer>(){

            public Integer initialValue(){

                return 0;            

            }

        };

        public void doSomething(){

            ad.set(ad.get()+1);

        }        

    }

    public class Count implements java.lang.Runnable{    

        String threadName;

        A a;

        public Count(String threadName,A a){

            this.threadName = threadName;

            this.a = a;

        }

        @Override

        public void run() {

            while(true){

                a.doSomething();

                System.out.println(this.threadName+"    "+a.ad.get());                

            }

        }        

    }

繼續閱讀