- ThreadLocal
很多地方叫做线程本地变量,也有些地方叫做线程本地存储,ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
它本身是为线程安全和某些特定场景的问题而设计的。
先了解一下ThreadLocal类提供的几个方法:
public T get() { }
public void set(T value) { }
public void remove() { }
protected T initialValue() { }
get()方法是用来获取ThreadLocal在当前线程中保存的变量副本,set()用来设置当前线程中变量的副本,remove()用来移除当前线程中变量的副本,initialValue()是一个protected方法,一般是用来在使用时进行重写的,它是一个延迟加载方法.
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(t)方法获取到一个map,map的类型为ThreadLocalMap。然后接着下面获取到<key,value>键值对,注意这里获取键值对传进去的是 this,而不是当前线程t。如果获取成功,则返回value值。 如果map为空,则调setInitialValue方法返回value。
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
ThreadLocalMap的Entry继承了WeakReference,并且使用ThreadLocal作为键值。
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;
}
}
setInitialValue() 如果map不为空,就设置键值对,为空,再创建Map,看一下createMap的实现
/**
* Variant of set() to establish initialValue. Used instead
* of set() in case user has overridden the set() method.
*
* @return the initial value
*/
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);
return value;
}
首先,在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本(即T类型的变量)。 初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。
ThreadLocal的应用场景:
最常见的ThreadLocal使用场景为 用来解决数据库连接、Session管理等。
实践:
public class ThreadLocalTest {
private static final ThreadLocal<Integer> LOCAL = new ThreadLocal(){
// 覆盖初始化方法
@Override
public Integer initialValue() {
return 0;
}
};
// 下一个序列号
public int getNextNum() {
LOCAL.set(LOCAL.get() + 1);
return LOCAL.get();
}
private static class TestClient extends Thread {
private ThreadLocalTest sn;
public TestClient(ThreadLocalTest sn) {
this.sn = sn;
}
// 线程变量加加
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() +" Num:" + sn.getNextNum());
}
}
}
public static void main(String[] args) {
ThreadLocalTest test = new ThreadLocalTest();
new TestClient(test).start();
new TestClient(test).start();
new TestClient(test).start();
}
}
测试结果:
- 编写工具类实现数据在同一个线程上的共享
public class ThreadUtils {
private static final ThreadLocal<Object> LOCAL = new ThreadLocal<>();
public static void setValue( Object value ){
LOCAL.set(value);
}
public static Object getValue(){
return LOCAL.get();
}