高并发---threadlocal

核心思想:

每个线程存有一个map,这个map里面数组存储entry,是键threadlocal对应的value。意思一个线程对应多个键threadlocal对应多个value;(看源码区分this和thread.currentthread,一个是字节码对象一个是当前线程对象)

核心元素:只有一点

ThreadLocal.ThreadLocalMap

下面三行代码来自Thread

/* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    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());                
            }
        }        
    }
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值