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