zoukankan      html  css  js  c++  java
  • ThreadLocal缺陷以及处理

    分析过ThreadLocal源码源码的人都知道,ThreadLocal的设计的确巧妙,但是它也有一个缺陷:可能会引起内存泄漏;ThreadLocalMap中key维护着一个weakReference,它在下次GC之前会被清理,如果Value仍然保持着外部的强引用,该ThreadLocal没有再进行set,get或者remove操作,时间长了就可能导致OutOfMemoryError .

    lucene中的类CloseableThreadLocal对ThreadLocal做了处理,优化了其缺陷.学习一下:
    当执行CloseableThreadLocal.set(T)时,内部其实只是把值赋给内部的ThreadLocal对象,即执行ThreadLocal.set(new WeakReference(T)),将T包装成弱引用对象,目的就是当内存不足时,jvm可以回收此对象.

    浙江引入一个新的问题:当前线程还存活着的时候,因为内存不足而回收了弱引用对象,这样会在下次调用get()时取不到值返回null,这是不可接受的.所以CloseableThreadLocal在内部还创建了一个私有的Map,WeakHashMap<Thread, T>,当线程只要存活时,则T就至少有一个引用存在,所以不会被提前回收。同时要注意另一个问题,要对WeakHashMap的操作使用synchronized做同步.

    
    package org.apache.lucene.util;
    
    
    import java.io.Closeable;
    import java.lang.ref.WeakReference;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.WeakHashMap;
    import java.util.concurrent.atomic.AtomicInteger;
    
    /** Java's builtin ThreadLocal has a serious flaw:
     *  it can take an arbitrarily long amount of time to
     *  dereference the things you had stored in it, even once the
     *  ThreadLocal instance itself is no longer referenced.
     *  This is because there is single, master map stored for
     *  each thread, which all ThreadLocals share, and that
     *  master map only periodically purges "stale" entries.
     *
     *  While not technically a memory leak, because eventually
     *  the memory will be reclaimed, it can take a long time
     *  and you can easily hit OutOfMemoryError because from the
     *  GC's standpoint the stale entries are not reclaimable.
     * 
     *  This class works around that, by only enrolling
     *  WeakReference values into the ThreadLocal, and
     *  separately holding a hard reference to each stored
     *  value.  When you call {@link #close}, these hard
     *  references are cleared and then GC is freely able to
     *  reclaim space by objects stored in it.
     *
     *  We can not rely on {@link ThreadLocal#remove()} as it
     *  only removes the value for the caller thread, whereas
     *  {@link #close} takes care of all
     *  threads.  You should not call {@link #close} until all
     *  threads are done using the instance.
     *
     * @lucene.internal
     */
    
    public class CloseableThreadLocal<T> implements Closeable {
    	//将值T用弱引用包裹,
      private ThreadLocal<WeakReference<T>> t = new ThreadLocal<>();
    
      // Use a WeakHashMap so that if a Thread exits and is
      // GC'able, its entry may be removed:
      //使用WeakHashMap,这样如果一个Thread退出并且是可GC,它Entry可能将被删除:
      //亦即WeakHashMap<Thread, T>,当线程只要存活时,则T就至少有一个引用存在,所以不会被提前回收
      private Map<Thread,T> hardRefs = new WeakHashMap<>();
      
      // Increase this to decrease frequency of purging in get:
      private static int PURGE_MULTIPLIER = 20;
    
      // On each get or set we decrement this; when it hits 0 we
      // purge.  After purge, we set this to
      // PURGE_MULTIPLIER * stillAliveCount.  This keeps
      // amortized cost of purging linear.
      private final AtomicInteger countUntilPurge = new AtomicInteger(PURGE_MULTIPLIER);
    
      protected T initialValue() {
        return null;
      }
      
      public T get() {
        WeakReference<T> weakRef = t.get();
        if (weakRef == null) {
          T iv = initialValue();
          if (iv != null) {
            set(iv);
            return iv;
          } else {
            return null;
          }
        } else {
          maybePurge();
          return weakRef.get();
        }
      }
    
      public void set(T object) {
    
        t.set(new WeakReference<>(object));
    	//使用synchronized同步
        synchronized(hardRefs) {
          hardRefs.put(Thread.currentThread(), object);
          maybePurge();
        }
      }
    
      private void maybePurge() {
        if (countUntilPurge.getAndDecrement() == 0) {
          purge();
        }
      }
    
      // Purge dead threads
      private void purge() {
        synchronized(hardRefs) {
          int stillAliveCount = 0;
          for (Iterator<Thread> it = hardRefs.keySet().iterator(); it.hasNext();) {
            final Thread t = it.next();
            if (!t.isAlive()) {
              it.remove();
            } else {
              stillAliveCount++;
            }
          }
          int nextCount = (1+stillAliveCount) * PURGE_MULTIPLIER;
          if (nextCount <= 0) {
            // defensive: int overflow!
            nextCount = 1000000;
          }
          
          countUntilPurge.set(nextCount);
        }
      }
    
      @Override
      public void close() {
        // Clear the hard refs; then, the only remaining refs to
        // all values we were storing are weak (unless somewhere
        // else is still using them) and so GC may reclaim them:
        hardRefs = null;
        // Take care of the current thread right now; others will be
        // taken care of via the WeakReferences.
        if (t != null) {
          t.remove();
        }
        t = null;
      }
    }
    
    
  • 相关阅读:
    [LeetCode] Power of Three 判断3的次方数
    [LeetCode] 322. Coin Change 硬币找零
    [LeetCode] 321. Create Maximum Number 创建最大数
    ITK 3.20.1 VS2010 Configuration 配置
    VTK 5.10.1 VS2010 Configuration 配置
    FLTK 1.3.3 MinGW 4.9.1 Configuration 配置
    FLTK 1.1.10 VS2010 Configuration 配置
    Inheritance, Association, Aggregation, and Composition 类的继承,关联,聚合和组合的区别
    [LeetCode] Bulb Switcher 灯泡开关
    [LeetCode] Maximum Product of Word Lengths 单词长度的最大积
  • 原文地址:https://www.cnblogs.com/DiZhang/p/12544869.html
Copyright © 2011-2022 走看看