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方法清除数据。