zoukankan      html  css  js  c++  java
  • ThreadLocal

    ThreadLocal

    ThreadLocal存储当前线程私有的数据

    Thread类中有一个默认(包级别)访问权限的字段:ThreadLocals,它是ThreadLocalMap类型的。

    ThreadLocal.ThreadLocalMap threadLocals = null;
    

    ThreadLocalMap是ThreadLocal的静态内部类。key是弱引用

    set操作

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);//以ThreadLocal对象为Key,value为值
        else
            createMap(t, value);
    }
    

    其中主要的操作:

    1. 获取当前线程的ThreadLocalMap对象:ThreadLocalMap map = getMap(t);

      ThreadLocalMap getMap(Thread t) {
          return t.threadLocals;
      }
      
    2. 在map中设置当前的key和value,其实就是在table数组的对应位置放置一个Entry,这个位置是通过key的哈希值与数组长度取模确定的,如果发生冲突,就探测下一个位置。

       private void set(ThreadLocal<?> key, Object value) {
              Entry[] tab = table;
              int len = tab.length;
              int i = key.threadLocalHashCode & (len-1);  
      	//如果这个位置已经存在Entry对象:
      		//1.如果存在的Key就是要set的key,那么更新对应的value值
      		//2.如果key==null,这是因为在Entry中弱引用的ThreadLocal已经被垃圾回收,但是还存在旧的value没有清理,需要替换旧的Entry
      		//否则,就采用开放地址法,探测下一个位置(源码其实就是i+1):i = nextIndex(i, len)是否还是e!=null,进行下一轮循环,直到e==null
      	//如果不存在Entry对象,就新建Entry(key,value)
          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();
      }
    
    1. 如果不存在ThreadLocalMap,就新建Map

      • ThreadLocalMap里面的table数组的负载因子是2/3,如果超过threshold,会先清理无用的Entry,如果还不够就两倍扩容
      private void setThreshold(int len) {
          threshold = len * 2 / 3;
      }
      

    get操作

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);\1.
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);\2.
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;\3.
                return result;
            }
        }
        return setInitialValue();
    }
    

    其中的主要操作:

    1. 获取当前线程对应的ThreadLocalMap:ThreadLocalMap map = getMap(t);

      ThreadLocalMap getMap(Thread t) {
          return t.threadLocals;
      }
      
    2. 根据ThreadLocalMap的getEntry(key)方法,以当前的ThreadLocal作为key获取Entry对象。在ThreadLocalMap类中有一个Entry类型的数组,数组每个位置都是Entry类型的数据,这里就是根据Key的哈希码与数组长度取模得到当前Key在数组中的位置,获取对应的Entry对象。

      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);
      }
      
    3. Entry的value就是需要的值:T result = (T)e.value;

    总结:

    • Thread类中有ThreadLocalMap类型的属性ThreadLocals,而ThreadLocalMap是ThreadLocal的静态内部类,它内部有一个静态内部类Entry,继承于WeakReference,Entry里面的key就是弱引用的ThreadLocal对象,value是设定的值。有一个Entry类型的table数组用于存放所有线程对应的Entry,当前线程对应的Entry在数组中的位置是通过key的哈希值与数组长度取模确定的。set和get方法就是从table数组中合适的位置放入Entry或者是获取value。

    弱引用问题:

    每个Thread内部都维护一个ThreadLocalMap数据结构,Map的Key就是ThreadLocal,ThreadLocal内部存储实体结构Entry<ThreadLocal, T>继承自java.lan.ref.WeakReference,这样当ThreadLocal不再被引用时,因为弱引用机制原因,垃圾回收时,jvm会自动回收弱引用指向的实例内存,即其线程内部的ThreadLocalMap会释放其对ThreadLocal的引用回收ThreadLocal对象,而非整个Entry,所以线程变量中的值T对象还是在内存中存在的,所以内存泄漏的问题还没有完全解决。应该尽量用threadLocal.remove()来清除无用的Entry。

  • 相关阅读:
    docker容器的应用
    KVM虚拟机迁移
    centos6.5虚拟机快照技术
    centos6.5网络虚拟化技术
    centos6.5制作OpenStack云平台Windows7镜像
    centos6.5远程桌面连接(VNCSPice)
    centos6.5kvm虚拟化技术
    centos7安装Jenkins及其卸载(yum和rpm安装)
    CentOS 7安装JDK
    [leetcode]Reverse Nodes in k-Group
  • 原文地址:https://www.cnblogs.com/learnjavajava/p/14901375.html
Copyright © 2011-2022 走看看