Java 之 ThreadLocal 详解

作者: jwfy | 来源:发表于2019-02-05 23:02 被阅读2次

ThreadLocal 在java中是充当“线程本地变量”使用的,在每个线程都会创建该对象从而实现线程之间的隔离,具有一定程度上的线程安全(也就是说不是绝对的线程安全,后面会细说这一点)

public T get()   // 获取当前线程的threadlocal存储的数据
public void set(T value)  // 为当前线程的threadlocal设置数据
public void remove()  // 移除threadlocal数据
protected T initialValue()  // 保护方法,构建初始值

接下来依次分析每个方法具体实现细节

public T get() {
    Thread t = Thread.currentThread();
    // 获取当前执行线程t
    ThreadLocalMap map = getMap(t);
    // 这个getMap方法是获取当前线程t持有的ThreadLocalMap对象信息
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        // map的KEY 是 this 也就是当前这个threadLocal对象本身
        // map的VALUE 是 ThreadLocalMap.Entry 一个entry对象(初次看貌似和hashmap的entry类似)
        if (e != null) {
            // 如果当前的value有数据,则直接取出器entry的value对象,返回
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    // 其余情况全部默认返回初始化值数据,具体看下面的方法
    return setInitialValue();
}

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
    // 返回线程t的threadlocals参数信息
}

private T setInitialValue() {
    T value = initialValue();
    // 默认返回null
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    // 再次获取当前线程的threadlocals对象
    if (map != null)
        map.set(this, value);
        // 设置的值是key:this,value:null
    else
        createMap(t, value);
        // 把当前的值塞入到map中
    return value;
}

线程有一个名字叫threadlocals的Map对象,键值对数据是<ThreadLocal,Value>,在获取的时候先看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 原来和一般意义上的HashMap并没有太多的关系,只是采取了类似的名称而已,而且需要注意到其使用了WeakReference 弱引用,当发生GC操作的时候其会被回收操作

因为threadlocals是线程的一个对象,那么不同的线程map是也是完全隔离的,也正是因为这一点才使得其具备了一定程度的线程安全的特性,不过并不是严格意义上的线程安全。例如下面这个例子说明为什么不是绝对的线程安全

image

如上图所示在一个JVM里面包含了2个线程A和B,在各自线程中的threadlocals对象分别是a和b,age和sex参数都是基础类型,是完全隔离开的,但是却共同引用了同一个Student对象信息,那么就存在A线程修改Student信息 ,然后B获取的Student信息是A修改之后的可能,打破了线程隔离这个特性。

第一点结论:ThreadLocal不具备线程安全的特性,想要拥有线程安全,Value必须是不可变对象

上面有提到ThreadLocalMap中的Entry 是WeakReference<ThreadLocal<?>>弱引用,每次GC操作都会被回收的,使得KEY为NULL,从而ThreadLocalMap存在<NULL, Object> 的对象信息,从而出现内存泄露的问题

为了防止这种问题,所以在使用完threadlocal对象后,需要显示调用remove方法

 public void remove() {
     ThreadLocalMap m = getMap(Thread.currentThread());
     if (m != null)
         // 在没有被GC回收期,从ThreadLocalMap 中主动移除该数据
         m.remove(this);
}
 
 private void remove(ThreadLocal<?> key) {
    Entry[] tab = table;
    int len = tab.length;
    int i = key.threadLocalHashCode & (len-1);
    for (Entry e = tab[i];
         e != null;
         e = tab[i = nextIndex(i, len)]) {
        if (e.get() == key) {
            e.clear();
            expungeStaleEntry(i);
             // 如果在tab中找到了对应的键值对,则直接设置其引用为null
            // 也就是使得tab[i] = null ,使得GC能够回收干净,不出现内存泄露的问题
            return;
        }
    }
}

第二点结论:使用ThreadLocal完毕后确保不出现内存泄露的问题,需要显示调用remove方法

总结:ThreadLocal可以用来存储一些线程临时的不可变对象,并不能用来处理线程共享,需要明确注意其组成结构

相关文章

  • Java 之 ThreadLocal 详解

    1. 概念 ThreadLocal 用于提供线程局部变量,在多线程环境可以保证各个线程里的变量独立于其它线程里的变...

  • Java 之 ThreadLocal 详解

    ThreadLocal 在java中是充当“线程本地变量”使用的,在每个线程都会创建该对象从而实现线程之间的隔离,...

  • ThreadLocal 详解

    ThreadLocal 详解 1. 前言 ThreadLocal是java线程中的局部变量,变量作用域仅在当前线程...

  • Java ThreadLocal详解

    首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.s...

  • 死磕Java源码之ThreadLocal实现分析

    死磕Java源码之ThreadLocal实现分析 通俗的讲, ThreadLocal是Java里一种特殊的变量。每...

  • ThreadLocal和InheritableThreadLoc

    ThreadLocal详解 - 简书 InheritableThreadLocal详解 - 简书 ThreadLo...

  • Java-ThreadLocal详解

    1、ThreadLocal的作用为:为每一条线程分配独立的资源,与synchronized方式不同,ThreadL...

  • Java ThreadLocal类详解

    在Java的多线程并发执行过程中,为了保证多个线程对变量的安全访问,可以将变量放到ThreadLocal类型的对象...

  • java并发之ThreadLocal

    java并发之ThreadLocal 知识导读 ThreadLocal主要作用于线程的上下文,而不是线程安全,如果...

  • Java 动态代理

    java的动态代理机制详解 JDK动态代理详解 Java核心技术点之动态代理

网友评论

    本文标题:Java 之 ThreadLocal 详解

    本文链接:https://www.haomeiwen.com/subject/ehhbsqtx.html