zoukankan      html  css  js  c++  java
  • ThreadLocal刨根问底

    一、ThreadLocal使用场景

      在数据库使用connection对象时,每个客户都能使用自己的connection对象,防止出现客户ClientA操作关闭ClientB的connection连接对象。

      案例:https://zhuanlan.zhihu.com/p/82737256

    二、ThreadLocal中的remove()使用

      1,防止内存泄露

      2,线程不安全

        在ThreadLocal和线程池联合使用的时候,会出现下个业务请求复用到上一个线程的情况,导致使用相同的ThreadLocal执行不同的业务逻辑。

    public class ThreadLocalAndPool {
        private static ThreadLocal<Integer> variableLocal = ThreadLocal.withInitial(() -> 0);
        public static int get() {
            return variableLocal.get();
        }
        public static void remove() {
            variableLocal.remove();
        }
        public static void increment() {
            variableLocal.set(variableLocal.get() + 1);
        }
    
        public static void main(String[] args) {
            ExecutorService executorService = new ThreadPoolExecutor(2, 2, 0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(12));
            for (int i = 0; i < 5; i++) {
                executorService.execute(() -> {
                    long threadId = Thread.currentThread().getId();
    
                    int before = get();
                    increment();
                    int after = get();
                    System.out.println("threadid " + threadId + "  before " + before + ", after " + after);
                });
            }
            executorService.shutdown();
        }
    }
    threadid 12  before 0, after 1
    threadid 13  before 0, after 1
    threadid 12  before 1, after 2
    threadid 13  before 1, after 2
    threadid 12  before 2, after 3

    三、为什么会出现内存泄露?

      ThreadLocal在ThreadLocalMap中是以一个弱引用身份被Entry中的Key引用的,因此如果ThreadLocal没有外部强引用来引用它,那么ThreadLocal会在下次JVM垃圾收集时被回收。这个时候就会出现Entry中Key已经被回收,出现一个null Key的情况,外部读取ThreadLocalMap中的元素是无法通过null Key来找到Value的。因此如果当前线程的生命周期很长,一直存在,那么其内部的ThreadLocalMap对象也一直生存下来,这些null key就存在一条强引用链的关系一直存在:Thread --> ThreadLocalMap-->Entry(table)-->Value,这条强引用链会导致Entry不会回收,Value也不会回收,但Entry中的Key却已经被回收的情况,造成内存泄漏。

    四、为什么使用弱引用?

      从表面上看,发生内存泄漏,是因为Key使用了弱引用类型。但其实是因为整个Entry的key为null后,没有主动清除value导致。为什么使用弱引用而不是强引用?

    官方文档的说法:

    To help deal with very large and long-lived usages, the hash table entries use WeakReferences for keys.
    为了处理非常大和生命周期非常长的线程,哈希表使用弱引用作为 key。

    下面我们分两种情况讨论:

    key 使用强引用:引用的ThreadLocal的对象被回收了,但是ThreadLocalMap还持有ThreadLocal的强引用,如果没有手动删除,ThreadLocal不会被回收,导致Entry内存泄漏。
    key 使用弱引用:引用的ThreadLocal的对象被回收了,由于ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal也会被回收。value在下一次ThreadLocalMap调用set,get,remove的时候会被清除。

      比较两种情况,我们可以发现:由于ThreadLocalMap的生命周期跟Thread一样长,如果都没有手动删除对应key,都会导致内存泄漏,但是使用弱引用可以多一层保障:弱引用ThreadLocal不会内存泄漏,对应的value在下一次ThreadLocalMap调用set,get,remove的时候会被清除。

    因此,ThreadLocal内存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key的value就会导致内存泄漏,而不是因为弱引用。

  • 相关阅读:
    iaas,paas,saas理解
    July 06th. 2018, Week 27th. Friday
    July 05th. 2018, Week 27th. Thursday
    July 04th. 2018, Week 27th. Wednesday
    July 03rd. 2018, Week 27th. Tuesday
    July 02nd. 2018, Week 27th. Monday
    July 01st. 2018, Week 27th. Sunday
    June 30th. 2018, Week 26th. Saturday
    June 29th. 2018, Week 26th. Friday
    June 28th. 2018, Week 26th. Thursday
  • 原文地址:https://www.cnblogs.com/bbgs-xc/p/13436582.html
Copyright © 2011-2022 走看看