本篇要说的是ThreadLocal,这个玩意平时在项目中很少用到,但是却有极大的用处;平时在面试中也会经常问到这个问题。
本篇使用jdk1.8版本。
先来看看源码中的介绍吧,文档太多,就不全贴出来了
/**
* This class provides thread-local variables. These variables differ from
* their normal counterparts in that each thread that accesses one (via its
* {@code get} or {@code set} method) has its own, independently initialized
* copy of the variable. {@code ThreadLocal} instances are typically private
* static fields in classes that wish to associate state with a thread (e.g.,
* a user ID or Transaction ID).
*
* <p>For example, the class below generates unique identifiers local to each
* thread.
* A thread‘s id is assigned the first time it invokes {@code ThreadId.get()}
* and remains unchanged on subsequent calls.
在注释中也写明了使用方法:
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadId {
// Atomic integer containing the next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);
// Thread local variable containing each thread‘s ID
private static final ThreadLocal<Integer> threadId =
new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return nextId.getAndIncrement();
}
};
// Returns the current thread‘s unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
}
概括下大概吧:
ThreadLocal中的源码:
首先来看set方法:
public void set(T value) {
// 获取当前线程对象(所以多线程的情况下都是操作的当前线程)
Thread t = Thread.currentThread();
// 从当前线程对象中获取ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
// 如果ThreadLocalMap不为null,直接存入
if (map != null)
// key为当前Threadlocal实例
map.set(this, value);
else
//如果ThreadLocalMap为null,则为当前线程创建ThreadLocalMap对象
createMap(t, value);
}
接下来在看看get
方法:
public T get() {
// 获取当前线程对象
Thread t = Thread.currentThread();
// 获取当前线程对象的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
// 以当前ThreadLocal实例为key获取map中的Entry对象
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
再来看看上面两个方法中使用到的方法:
// 多个方法都会调用这个方法
ThreadLocalMap getMap(Thread t) {
// 返回传入线程的ThreadLocalMap
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
// 为线程threadLocals赋值(新建)
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
private T setInitialValue() {
// 调用initialValue方法,获取初始化值
T value = initialValue();
// 获取当前线程对象
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
protected T initialValue() {
return null;
}
小结1: 如果在初始化的时候没用重写这个方法,将会返回null,所以这个方法一般会在初始化ThreadLocal的代码中重写这个方法。
在看看Thread中的代码:
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
小结2:
从上面的代码中可以发现,set
方法通过Thread.currentThread()
来获取当前线程对象,从而操作当前线程对象中的threadLocals
,也就是说通过Threadlocal操作的数据是Thread负责维护的;而threadLocals
又是ThreadLocal
类中的一个静态内部类ThreadLocalMap
,其中key
为this
(这里就是当前的Threadlocal实例对象);
小结3:
Threadlocal
的大致理解就是:通过Threadlocal
的实例对象a
去操作当前Thread
中ThreadLocal.ThreadLocalMap
对象中key
为a
所对应的数据;最典型的应该就是数据库连接池了吧,如何处理线程与数据库连接之间的关系,应该就是用到的Threadlocal,具体的就得去谷歌或去看源码了。
这里说一下我所用到过的场景:
关于内存泄漏,其实我在使用过程中也遇到过一次;下面说一下场景吧:
下面是流程图:
我们使用ThreadLocal来对用户信息进行记录,对请求进行拦截,将用户信息存入,但是在有时候会出现游客模式下回显示已登录,还不是自己的账户;
为什么会出现这种情况呢?
解决办法:
参考文章:
ThreadLocal就是这么简单
Java ThreadLocal
Java进阶(七)正确理解Thread Local的原理与适用场景
原文:https://www.cnblogs.com/guoyuchuan/p/13113730.html