Java ThreadLocal 解析

什么是ThreadLocal

  早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路,使用这个工具类可以很简洁地编写出优美的多线程程序。
  当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
  从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。
  所以,在Java中编写线程局部变量的代码相对来说要笨拙一些,因此造成线程局部变量没有在Java开发者中得到很好的普及。

ThreadLocal 怎么使用

public class Tt {
    private static final ThreadLocal<String> holder = new ThreadLocal<>();

    public static void set(String toSave) {
        holder.set(toSave);
    }

    public static String get() {
        return holder.get();
    }

    public static void remove() {
        holder.remove();
    }
}


class Test {

    public static void main(String[] args) {
        Tt.set(Thread.currentThread().getName());

        System.out.println(Tt.get());
    }
}

ThreadLocal 原理是什么

  1. Thread 中维护了一个ThreadLocal.ThreadLocalMap的引用
	public
	class Thread implements Runnable {
	...
		/* ThreadLocal values pertaining to this thread. This map is maintained
	     * by the ThreadLocal class. */
	    ThreadLocal.ThreadLocalMap threadLocals = null;
    ...
    }
  1. ThreadLocalMap 是ThreadLocal的子类
  2. 看下ThreadLocal的set方法
/**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    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. 可以看到先从getMap(t)中获取ThreadLocalMap,如果map为null,则createMap(t, value)我们看下getMap(t)的代码:
/* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
/**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
  1. 可以看到返回的就是ThreadLocalMap,我们看下createMap(t, value)的代码:
	/**
     * Create the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the map
     */
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
	/**
       * Construct a new map initially containing (firstKey, firstValue).
       * ThreadLocalMaps are constructed lazily, so we only create
       * one when we have at least one entry to put in it.
       */
      ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
          table = new Entry[INITIAL_CAPACITY];
          int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
          table[i] = new Entry(firstKey, firstValue);
          size = 1;
          setThreshold(INITIAL_CAPACITY);
      }
  1. 在newThreadLocalMap的时候,将this传入了了构造器中,这个this就是我们在外面new的ThreadLocal,ThreadLocalMap中存入的是我们new出来的实例的hash值,本质上还是一个数组
  2. 至此,ThreadLocal在多线程中的工作原理就分析完了

避免内存泄漏

图

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

总结

  1. 每个Thread维护着一个ThreadLocalMap的引用
  2. ThreadLocalMap是ThreadLocal的内部类
  3. 调用ThreadLocal的set()方法时,实际上就是往ThreadLocalMap设置值,key是ThreadLocal对象,值是传递进来的对象
  4. 调用ThreadLocal的get()方法时,实际上就是往ThreadLocalMap获取值,key是ThreadLocal对象
    ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value,所以ThreadLocal能够实现“数据隔离”。
  5. 想要避免内存泄露就要手动remove()掉!
  6. 最后要记住的是:ThreadLocal设计的目的就是为了能够在当前线程中有属于自己的变量,并不是为了解决并发或者共享变量的问题
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值