从 心 理 解 JDK8的ThreadLocal

大家好,JDK1.8推出的ThreadLocal是经常被提问到的一个知识点,也是在项目中常用到的技术,所以在面试的时候经常被面试管问,请你说说自己对ThreadLocal的理解,提问的方式有很多,可能是由浅入深,因此只要我们理解透彻了这个技术,那么不管怎么问都能游刃有余。

笔者从以下几个方面来解析和理解ThreadLocal:

  1. ThreadLocal是什么?
  2. ThreadLocal如何使用?
  3. ThreadLocal源码解析。
  4. ThreadLocal内存泄漏问题的解析。

————————————————————————我是分割线———————————————————————————

ThreadLocal是什么?

根据名称可以看出 ThreadLocal被称为线程变量,意思是 ThreadLocal中填充的变量属于当前线程,而当前线程隔离了其他线程。在每个线程中, ThreadLocal为变量创建一个副本,然后每个线程可以访问自己内部的副本变量。

ThreadLocal如何使用?

我们可以将它作用与存储用户的信息。

 private static ThreadLocal<MiaoShaUser> userHolder = new ThreadLocal<MiaoShaUser>();

  /**
   * 通过方法向ThreadLocal中赋值
   * @param user
   */
  public static void setUser(MiaoShaUser user) {
    userHolder.set(user);
  }

  public static MiaoShaUser getUser() {
    return userHolder.get();
  }

创建一个ThreadLocal,写两个方法,一个set一个get,获取到用户数据的时候set以下,这样就可以在需要的时候去调用当前线程的用户数据,并且不会出现线程不安全的问题,但它还要一个严重的问题内存溢出,我们一会再说。

上面的使用我们只用到了两个方法,set()和get(),但其实它还有其他的方法。

 //初始化
protected T initialValue();
public T get();
public void set();
//删除
public void remove();

光从单词就能看出是非常好理解。

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);
    }

在 set方法中,我们可以看到,首先获取当前线程 t,然后调用 getMap获取 ThreadLocalMap,如果有 map,就把当前线程对象 t作为 key,把要存储的对象作为值存在 map中。当 Map不存在时对其进行初始化。

ThreadLocalMap是什么?先来看看源码。

static class ThreadLocalMap {

        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
//下面省略......

源码需要静心的去看,如果实在不想看就直接看我的总结吧==

实际上, ThreadLocalMap是 ThreadLocal的一个静态内部类,其中定义了一个 Entry来保存数据,还包括继承的弱引用。将 ThreadLocal用作 Entry内部的key,将我们设置的 value用作value。

还有一个getMap();

 /**
     * 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;
    }

当前线程 t被调用,它在当前线程 t中返回成员变量 threadLocals。事实上threadLocals就是 ThreadLocalMap
 

现在咱们简单的总结以下:

ThreadLocal本身是不存储值的,而是靠它的ThreadLocalMap,而ThreadLocalMap底层是靠的Entry存储数据,所以我们可以简单的来理解,

将ThreadLocal比作一条裤子,ThreadLocalMap比作一个兜,兜在裤子上,数据是放在兜里面的。要去获取数据就必须要通过这条裤子,兜的底层是Entry通过k-v的形式存储数据,k就是当前的裤子,v是你自己存储的数据(比如说钞票==,对象)。

 

ThreadLocal内存泄漏问题的解析。

前面我们说到它虽然线程安全,但是它存在一个问题那就是内存泄漏

首先我们要明白为什么会内存泄漏,前面也说了ThreaLocal是一个弱引用,什么是弱引用就是当它为null时候,就会被垃圾回收机制给带走,重点就是,如果我们的ThreadLocal突然为null,然后就被回收了,但此时我们的ThreadLocalMap它的生命周期是和Thread相同的,简单理解就是,裤子没了,兜还在,兜里面还有我们的数据,这就造成了内存泄漏。

如何解决那:我们必须在使用完ThreadLocal后,执行remove()方法,避免内存溢出。

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值