ThreadLocal类是什么:
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. 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).
For example, the class below generates unique identifiers local to each thread. A thread's id is assigned the first time it invokes 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();
}
}
Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).
oracle官方文档的解释,翻译过来大概的意思是说,ThreadLocal的作用就是为每一个线程保存一个局部变量,这样可以做到每个线程的变量能够互相隔离,彼此不能互相访问。其实ThreadLocal应该叫ThreadLocalVariable更合适,所以我们也不能单单从名字上来判断啦。
看一下ThreadLocal的API:
//Creates a thread local variable.
ThreadLocal()
//Returns the value in the current thread's copy of this thread-local variable.
T get()
//Returns the current thread's "initial value" for this thread-local variable.
protected T initialValue()
//Removes the current thread's value for this thread-local variable.
void remove()
//Sets the current thread's copy of this thread-local variable to the specified value.
void set(T value)
//Creates a thread local variable.
static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier)
目前先集中精力关注前五个方法,即构造方法,get()
initialValue()
remove()
和set(T value)
构造方法肯定不说用了。所以首先来看一下initialValue()方法的源码:
protected T initialValue() {
return null;
}
这个方法是提供给继承的子类复写用的,一般在某些特定的业务情景下,我们需要给ThreadLocal一个初始变量。
看一下set(T value)源码:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
包括其中的getMap()方法:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
这个方法就是通过获得当前线程来获得该线程所持有的ThreadLocal对象,然后通过getMap()方法来获得一个ThreadLocalMap对象,最后根据是否为空,来决定是直接set还是先create再set
接下来是get()方法:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
关键代码就是通过map.getEntry()来获得一个ThreadLocalMap.Entry,然后再取出其中的值
接下来是remove()方法:
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
调用ThreadLocalMap的remove方法,来移除掉该ThreadLocal所对应的局部变量
小结:
有关ThreadLocal的源码分析,进一步会涉及到ThreadLocalMap是如何进行set()的,如何get(),remove()的,其中会涉及到一些数据结构,比如继承自WeakReference的Entry数组,哈希值的计算等,不过在此没有再进行深入一布的分析。这次进行一波简单的分析,一来是为了给自己扫盲,而来是让自己对这个ThreadLocal有一个关注。写到这里,又去查了一下Spring框架是如何实现并发处理的,貌似还和这个ThreadLocal有关系,哈!好有意思,所以会考虑再跟Spring框架结合来一波分析。
动手实践
我发现自己还真和ThreadLocal杠上了。这不,结合着自己,自己用Java实现了一个简单的ThreadLocal类。毕竟动手操作一下,也有助于自己对其的理解嘛。
看过别人写的代码之后,我思考想了一下如果要实现这个简单的ThreadLocal,哪些点是核心,最为重要的。
想了一下,觉着线程和变量之间是一个K-V的关系,通过线程K就可以获得其变量,所以就需要用一个Map来封装这些数据。不过还有十分重要的一点就是,不同线程能够对该Map进行同步操作。
所以首先就需要声明一个Map:
private Map<Thread, Object> valueMap = Collections.synchronizedMap(new HashMap<Thread, Object>());
注意到该Map为 synchronizedMap
。不过至于synchronizedMap
是怎样的一个类,其实我也不懂。这里就先不说啦。
实现ThreadLocal的set()方法:
public void set(Object newValue) {
valueMap.put(Thread.currentThread(), newValue);
}
很简单,一个put方法就搞定了。
接下来实现ThreadLocal的get()方法:
public Object get() {
Thread thread = Thread.currentThread();
Object o = valueMap.get(thread);
if (o == null && !valueMap.containsKey(thread)) {
o = initialValue();
valueMap.put(thread, o);
}
return o;
}
如果该线程的ThreadLocal中还没有对象,则为其初始化一个值:null
public Object initialValue() {
return null;
}
接下来实现ThreadLocal的remove()方法:
public void remove() {
valueMap.remove(Thread.currentThread());
}
这样一来一个简单的ThreadLocal就搞定啦
ThreadLocal在Spring中的应用
总结了这么多ThreadLocal的作用,再细细考虑一下,ThreadLocal如果说要在Spring中起什么作用的话,就是利用"以空间换取时间"的方法很好了解决了线程同步访问问题。之前我们在解决同步问题,用的是同步块:Synchronized。Synchronized实现同步是"以时间换空间"的方法,不过相比于这个方法,效率较低。说到ThreadLocal在Spring的应用,将一些单例模式的bean绑定到了每一个线程上,比如对于数据库的连接、事务资源。
总结
在网上找了很多有关ThreadLocal在Spring中的应用,有用的不多,看完之后感觉收获也不是太大。这一块以后在深入学习Spring的过程中也要稍加留意!