JUC---ThreadLocal源码解析(JDK13)

java.util.concurrent包系列文章
JUC—ThreadLocal源码解析(JDK13)
JUC—ThreadPoolExecutor线程池源码解析(JDK13)
JUC—各种锁(JDK13)
JUC—原子类Atomic*.java源码解析(JDK13)
JUC—CAS源码解析(JDK13)
JUC—ConcurrentHashMap源码解析(JDK13)
JUC—CopyOnWriteArrayList源码解析(JDK13)
JUC—并发队列源码解析(JDK13)
JUC—多线程下控制并发流程(JDK13)
JUC—AbstractQueuedSynchronizer解析(JDK13)


ThreadLocal -> 线程本地变量

  • 强调同一个请求内(同一个线程内)不同方法间的共享
  • 让某个需要用到的对象在线程间隔离(每个线程都有自己独有自己独立的对象)

ThreadLocal的优点

  • 达到线程安全
  • 不需要加锁
  • 提高执行效率 高效利用内存
  • 节省开销 避免传参的麻烦

Thread、ThreadLocal、ThreadLocalMap三者的关系

每个Thread对象中都持有一个ThreadLocalMap对象,可以看做是一个Map,key就是ThreadLocal,value就是放进去的对象。

在这里插入图片描述

所谓线程本地变量,其实就是把值存储在当前线程的:

在这里插入图片描述
在这里插入图片描述

ThreadLocal的重要方法

public void set(T value) {
    // 获取当前线程
    Thread t = Thread.currentThread();
    // 获取当前线程的ThreadLocalMap 
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        // 把当前ThreadLocal作为key
        map.set(this, value);
    } else {
        // map为空就初始化并设置值
        createMap(t, value);
    }
}

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}
public T get() {
     // 获取当前线程
    Thread t = Thread.currentThread();
    // 获取当前线程的ThreadLocalMap 
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        // 把当前ThreadLocal作为key,差U型你
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    // map为空说明还未初始化
    return setInitialValue();
}

private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        map.set(this, value);
    } else {
        createMap(t, value);
    }
    if (this instanceof TerminatingThreadLocal) {
        TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);
    }
    return value;
}

protected T initialValue() {
    return null;
}

public void remove() {
    // 获取当前线程的ThreadLocalMap 
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null) {
        // m不为空则移除元素
        m.remove(this);
    }
}

从get()、set()、remove()方法都可以看到,就是以当前线程为变量为key存储线程本地变量。每一个线程都有自己的变量值。从而没有线程安全问题。线程退出的时候必须调用remove()方法,防止溢出。

业务场景:在拦截器preHandle()方法中验证用户身份,验证通过,则调用set()方法把当前用户信息存入ThreadLocal。放行当前请求,当前请求进入业务系统,执行了各种业务处理之后,在拦截器的postHandle()和afterCompletion()方法中一定要调用remove()方法清楚当前线程本地变量。

在这里插入图片描述

ThreadLocal在Spring中的典型例子:RequestContextHolder、DateTimeContextHolder

public abstract class RequestContextHolder  {

   private static final boolean jsfPresent =
         ClassUtils.isPresent("javax.faces.context.FacesContext", RequestContextHolder.class.getClassLoader());

   private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
         new NamedThreadLocal<>("Request attributes");

   private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
         new NamedInheritableThreadLocal<>("Request context");
         
         、、、省略、、、
         
         @Nullable
        public static RequestAttributes getRequestAttributes() {
           RequestAttributes attributes = requestAttributesHolder.get();
           if (attributes == null) {
              attributes = inheritableRequestAttributesHolder.get();
           }
           return attributes;
        }
}         

  • 我的公众号:Coding抠腚
  • 一个被电焊耽误的Java程序员。偶尔发发自己最近学到的干货。学习路线,经验,技术分享。技术问题交流探讨。
    Coding抠腚
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章