天天看点

高并发---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());                

            }

        }        

    }

继续阅读