关于java:详解ThreadLocal原理及内存泄漏

8次阅读

共计 1955 个字符,预计需要花费 5 分钟才能阅读完成。

1. ThreadLocal 作用

ThreadLocal 的作用是使得每个线程 都能领有各自独立的对象正本 ,假如多个线程领有同一个实例,ThreadLocal<T> 类型的变量在每个线程中都有一个正本,从而为变量提供了 线程间隔离 的作用。

2. ThreadLocal 实例


public class ThreadLocalDemo implements Runnable{private static ThreadLocal<People> local = new ThreadLocal<>();

    public static void main(String[] args) throws InterruptedException {ThreadLocalDemo localDemo = new ThreadLocalDemo();
        Thread thread1 = new Thread(localDemo);
        thread1.start();

          Thread.sleep(1000);
      
        Thread thread2 = new Thread(localDemo);
        thread2.start();}

    @Override
    public void run() {People people = local.get();
        if (people==null) {people = new People();
            people.setName("djh");
            people.setAge(new Random().nextInt());
            local.set(people);
        }
        System.out.println(people);
    }
}

public class People {
    String name;
    int age;

    public void setName(String name) {this.name = name;}

    public void setAge(int age) {this.age = age;}

}

在该例子中,thread1thread2 领有同一个对象 localDemo,如果不应用ThreadLocal 包装,则两个线程拜访的 people 应该是同一个。而在咱们应用 ThreadLocal 包装后,运行后果如下:

bean.People@6fb35e7d
bean.People@614b201a

两者拜访的 people 是不一样的两个对象,而这就是 ThreadLocal 的作用,提供了 变量线程间的隔离能力。

3. 源码解析

查看 ThreadLocal 的 set 办法,

public void set(T value) {Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}
  1. 先获取以后线程;
  2. 获取以后线程的 threadLocals 变量(是一个 ThreadLocalMap 类型的 Map);
  3. 如果 map 不为空,为这个 map 插入 < 以后 ThreadLocal 对象,值 > 的键值对;
  4. 如果该 map 为空,则创立一个一个 ThreadLocalMap 对象,t.threadLocals = new ThreadLocalMap(this, firstValue)

因为每个线程都有各自的 threadLocals,所以多个线程运行时 通过第二步拿到的 map 是不一样的,这样就实现了线程间隔离。

4. ThreadLocal 的内存透露问题

由第 3 节能够得悉,数据理论存在每个线程的 threadLocals 成员(即一个ThreadLocalMap 对象)里,ThreadLocalMap 和一般的 map 一样应用 Entry 数组存储数据,这个 Entry 非凡的中央在于,它继承了弱援用,即该 Entry 的 key 是一个 ThreadLocal 对象的弱援用。

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal<?> k, Object v) {super(k);
        value = v;
    }
}

弱援用的特点是,如果某个对象只被弱援用关联,在下一次 GC 时该对象就会被回收。此时某个 ThreadLocal 类型变量应用完被咱们置为 null,此时 Entry 中关联的 Key 会变成 null,但 Value 因为是强援用,且运行的线程指向他,因而 Value 局部产生了内存透露。

线程执行结束被回收时 Value 会随之回收,但当咱们应用线程池时,因为线程会复用,因而 Value 会始终存在,这就产生了更为严重的内存透露。

5. 参考资料

  1. 正确理解 Thread Local 的原理与实用场景
  2. ThreadLocal 内存透露问题深入分析
正文完
 0