• java中的引用与ThreadLocal


    ThreadLocal

    前几天看了@华为kim的threadlocal的博文深有感触,所有在这再次总结一下我对threadlocal的源码理解,以及内部机制。 

    数据结构

    下面看一下threadlocal的数据结构:每一个Thread内部都有一个 ThreadLocal.ThreadLocalMap threadLocals 对象 , 而ThreadLocalMap 中,维护着一个Entry[]数组,每个Entry对象,包含一个弱引用的ThreadLocal和一个value。

    内部机制

    SET操作

     public void set(T value) {
            //获取当前线程
            Thread t = Thread.currentThread();
            //根据当前线程,找到线程内部的ThreadLocalMap
            ThreadLocalMap map = getMap(t);
            if (map != null)  //赋值  当前的ThreadLocal  和  value
                map.set(this, value);
            else   //创建线程内部的ThreadLocalMap
                createMap(t, value);
        }    

    GET操作

    public T get() {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null) {
                //根据当前ThreadLocal为key,获取对应的Entry,并获取value
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    T result = (T)e.value;
                    return result;
                }
            }
            //返回默认值
            return setInitialValue();
        }

    原理分析

    1)为什么ThreadLocalMap要用WeakReference来封装key?

    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;
        Entry(ThreadLocal<?> k, Object v) {
             super(k);
             value = v;
             }
        }
    WeakReference有以下特性:
      当一个对象A只有WeakReference引用指向它是,那么A在下一次gc的时候就会被回收掉。想象下,如果ThreadLocalMap中某个key已经不用了,最终只会有一个WeakReference指向它,
      这个key自然就可以被回收掉,不会一直停留在ThreadLocalMap中。(对引用将在后面详细介绍)
     
    如果ThreadLocal被回收掉了,那么value怎么回收?
      在ThreadLocalMap的get和set方法中,根据ThreadLocal经过hash,获取到的Entry,如果Entry的key=null,执行expungeStaleEntry(i)方法,该方法的主要操作把哈希表当前位置的无用数据清理掉(当然还有别的操作)。
     
    总之,假设某个threadlocal对象无效,这个对象本身会在下次gc被回收,对应的value值也会在某次ThreadLocal调用中被释放;如果某个thread死掉了,它对应的threadlocal内容自动释放。
     
    2)为什么要用开放地址实现Hash冲突呢?
    ThreadLocalMap的 Entry[] table 表不同于HashMap的链表法解决冲突。

      a. 节省内存空间,链表使用的空间大于数组;
      b. threadLocalMap设计的哈希key可以尽可能避免哈希冲突;
      c. 清理数据效率高,毕竟遍历数组比遍历链表效率高;

     java中的引用

     在深入理解JVM一书中,谈及了强引用,软引用,弱引用,虚引用。但笔者没有当时深入研究,下面是对java中引用的详细总结。

    WeakHashMap

    在介绍引用之前,先来看一下WeakHashMap的实现原理,先给出下面的测试用例,

    public class test {
      public static void main(String[] args) {
        Map<Integer, Object> map = new HashMap<>();
        for (int i = 0; i < 10000; i++) {
          Integer ii = new Integer(i);
          map.put(ii, new byte[i]);
        }
      }
    }
    
    //内存溢出
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at com.test.test.main(test.java:18)
    
    //将HashMap改成WeakHashMap
    无任何报错

     实际上,WeakHashMap指定了弱键,当只有一个弱引用指向该key,那么在内存不足,下次GC的时候清除该域。此外,在WeakHashMap的set(),get(),size()操作中,都间接或者直接调用了expungeStaleEntries()方法,以清理持有弱引用的key的表项。 

      private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
            V value;
            final int hash;
            Entry<K,V> next;
    
            /**
             * Creates new entry.
             */
            Entry(Object key, V value,
                  ReferenceQueue<Object> queue,
                  int hash, Entry<K,V> next) {
                super(key, queue);
                this.value = value;
                this.hash  = hash;
                this.next  = next;
            }

     软引用与弱引用

    引用的使用十分广泛,对强引用和虚引用就不一一介绍了,下面重点介绍两个引用,软引用和弱引用。

    看下面示例:

    public class test {
      public static void main(String[] args) {
        /*Test1*/
        Pojo pojo = new Pojo("Test1");
        WeakReference<Pojo> sr = new WeakReference<Pojo>(pojo);
        //如果添加下面的软应引用,而软引用只有在内存不足的情况下才会删除对象,所有会打印 Test1
        //SoftReference<Pojo> sf = new SoftReference<Pojo>(pojo);
        pojo = null;
        //此时只有一个弱引用指向对象
        System.gc();
        System.out.println(sr.get());   //null
    
      }
      static class Pojo {
        String value;
        Pojo(String value) {
          this.value = value;
        }
        public String toString() {
          return this.value;
        }
      }
    }

    总结

    WeakReference:每次gc的时候,如果一个对象A只被WeakReference直接引用,那么A就可以被回收掉;
    SoftReference:每当内存不足的时候(其实和WeakReference差不多),如果一个对象A只被SoftReference或WeakReference直接引用,那么A就可以被回收掉;
    注意:内存不足或者gc的时候,回收的不是reference对象本身,而是reference所引用的对象。



  • 相关阅读:
    多进程访问同一文件问题
    在主页面中实现Iframe中的子页面的切换
    在任务栏显示地理坐标
    ajax异步调用过程
    实现DIV标签的显示与隐藏
    使用supermap的心得
    nokia手机问题
    sys.webforms.pagerequestmanagerservererrorexception回发或回调参数无效
    AjaxScript地图打印[转]
    js获取下拉框中的值
  • 原文地址:https://www.cnblogs.com/gaojy/p/7506286.html
Copyright © 2020-2023  润新知