Java中的ThreadLocal
Java中的ThreadLocal
变量值的共享可以使用public static变量的形式,所有线程可以共享一个变量。如果我们想实现一个线程都有自己的共享变量该如何解决呢,这里就要用到Java中的ThreadLocal类。
类ThreadLocal主要解决的就是每个线程绑定自己的值,可以将ThreadLocal类比喻成全局存放数据的盒子,盒子中可以存储每个线程的私有数据
我们先看下列代码
public static void main(String[] args) throws InterruptedException {
ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 0;
}
};
threadLocal.set(100);
System.out.println("主线程中的值:"+threadLocal.get());
Thread t = new Thread(){
@Override
public void run() {
threadLocal.set(200);
System.out.println("线程t中的值:"+threadLocal.get());
}
};
t.start();
}
代码定义了一个ThreadLocal对象,然后在主线程中,给ThreadLocal对象放入100,而线程t则放入200,并打印。结果如下
可见,每个线程都取到了自己存入的那个值。那么它到底是怎么实现的呢,让我们来分析分析吧
首先先介绍一下ThreadLocal的一些主要方法
1、initalValue方法
protected T initialValue() {
return null;
}
initalValue方法,表示默认值,可见是null,但为了安全起见,我们在初始化ThreadLocal对象时采用匿名内部类来重写这个方法,以防止空指针异常。也就是这样:
ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 0;
}
};
2、get方法
public T get() {
//获取当前线程对象
Thread t = Thread.currentThread();
//取出线程类中的ThreadLocalMap
ThreadLocal.ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocal.ThreadLocalMap.Entry e = map.getEntry(this);
//如果Map不为空并且该线程确实存入了变量,则返回存入的变量
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//Map为空,返回默认值
return setInitialValue();
}
这里要说到,每个线程中都维护了一个ThreadLocalMap对象,这个Map就存储着你所存入的值,下面会说到。
3、set方法
public void set(T value) {
//获取当前线程对象
Thread t = Thread.currentThread();
//根据线程获取线程中的ThreadLocalMap
ThreadLocal.ThreadLocalMap map = getMap(t);
//如果map不为空则直接存入值
if (map != null)
map.set(this, value);
//如果map为空,则创建Map并存入值
else
createMap(t, value);
}
4、remove方法
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
remove是移除所存入的值
5、getMap方法
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
根据线程去获得线程中的ThreadLocalMap对象
我们可以打开Thread类源码
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null;
这就是Thread类中的ThreadLocalMap对象,默认为空。只有你在使用ThreadLocal为线程保存私有属性时才会创建。
6、createMap方法
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
将线程中的ThreadLocalMap对象实例化并写入存入值
接下来就要看存储数据的ThreadLocalMap
static class ThreadLocalMap {
//Map节点,注意key是ThreadLocal而不是Thread
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//构造方法
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new ThreadLocal.ThreadLocalMap.Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new ThreadLocal.ThreadLocalMap.Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
}
Map很简单,只不过要注意的是Map的键是ThreadLocal而不是Thread。
总结
1、ThreadLocal是为了解决每个线程绑定自己的值
2、ThreadLocal默认值为null,所以有时候为了安全,初始化时使用匿名内部类来重写initalValue方法
3、每个线程对象内部都维护了一个ThreadLocalMao对象
4、ThreadLocalMap的键是ThreadLocal而不是Thread
5、ThreadlLocal可能会发生内存泄漏,所以在使用完ThreadLocal之后调用remove方法清除数据。
