ThreadLocal由Java界的两个大师级的作者编写,Josh Bloch和Doug Lea。Josh Bloch是JDK5语言增强、Java集合(Collection)框架的创办人以及《Effective Java》系列的作者。Doug Lea是JUC(java.util.concurrent)包的作者,Java并发编程的泰斗。所以,ThreadLocal的源码十分值得学习。
ThreadLocal的原理ThreadLocal虽然叫线程本地(局部)变量,但是实际上它并不存放任何的信息,可以这样理解:它是线程(Thread)操作ThreadLocalMap中存放的变量的桥梁。它主要提供了初始化、set()、get()、remove()几个方法。这样说可能有点抽象,下面画个图说明一下在线程中使用ThreadLocal实例的set()和get()方法的简单流程图。
假设我们有如下的代码,主线程的线程名字是main(也有可能不是main):
public class Main { private static final ThreadLocal<String> LOCAL = new ThreadLocal<>(); public static void main(String[] args) throws Exception{ LOCAL.set("doge"); System.out.println(LOCAL.get()); } }线程实例和ThreadLocal实例的关系如下:
上面只描述了单线程的情况并且因为是主线程忽略了Thread t = new Thread()这一步,如果有多个线程会稍微复杂一些,但是原理是不变的,ThreadLocal实例总是通过Thread.currentThread()获取到当前操作线程实例,然后去操作线程实例中的ThreadLocalMap类型的成员变量,因此它是一个桥梁,本身不具备存储功能。
ThreadLocal源码分析对于ThreadLocal的源码,我们需要重点关注set()、get()、remove()几个方法。
ThreadLocal的内部属性 //获取下一个ThreadLocal实例的哈希魔数 private final int threadLocalHashCode = nextHashCode(); //原子计数器,主要到它被定义为静态 private static AtomicInteger nextHashCode = new AtomicInteger(); //哈希魔数(增长数),也是带符号的32位整型值黄金分割值的取正 private static final int HASH_INCREMENT = 0x61c88647; //生成下一个哈希魔数 private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); }这里需要注意一点,threadLocalHashCode是一个final的属性,而原子计数器变量nextHashCode和生成下一个哈希魔数的方法nextHashCode()是静态变量和静态方法,静态变量只会初始化一次。换而言之,每新建一个ThreadLocal实例,它内部的threadLocalHashCode就会增加0x61c88647。举个例子:
//t1中的threadLocalHashCode变量为0x61c88647 ThreadLocal t1 = new ThreadLocal(); //t2中的threadLocalHashCode变量为0x61c88647 + 0x61c88647 ThreadLocal t2 = new ThreadLocal(); //t3中的threadLocalHashCode变量为0x61c88647 + 0x61c88647 + 0x61c88647 ThreadLocal t3 = new ThreadLocal();threadLocalHashCode是下面的ThreadLocalMap结构中使用的哈希算法的核心变量,对于每个ThreadLocal实例,它的threadLocalHashCode是唯一的。
内部类ThreadLocalMap的基本结构和源码分析ThreadLocal内部类ThreadLocalMap使用了默认修饰符,也就是包(包私有)可访问的。ThreadLocalMap内部定义了一个静态类Entry。我们重点看下ThreadLocalMap的源码,先看成员和结构部分:
/** * ThreadLocalMap是一个定制的散列映射,仅适用于维护线程本地变量。 * 它的所有方法都是定义在ThreadLocal类之内。 * 它是包私有的,所以在Thread类中可以定义ThreadLocalMap作为变量。 * 为了处理非常大(指的是值)和长时间的用途,哈希表的Key使用了弱引用(WeakReferences)。 * 引用的队列(弱引用)不再被使用的时候,对应的过期的条目就能通过主动删除移出哈希表。 */ static class ThreadLocalMap { //注意这里的Entry的Key为WeakReference<ThreadLocal<?>> static class Entry extends WeakReference<ThreadLocal<?>> { //这个是真正的存放的值 Object value; // Entry的Key就是ThreadLocal实例本身,Value就是输入的值 Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } //初始化容量,必须是2的幂次方 private static final int INITIAL_CAPACITY = 16; //哈希(Entry)表,必须时扩容,长度必须为2的幂次方 private Entry[] table; //哈希表中元素(Entry)的个数 private int size = 0; //下一次需要扩容的阈值,默认值为0 private int threshold; //设置下一次需要扩容的阈值,设置值为输入值len的三分之二 private void setThreshold(int len) { threshold = len * 2 / 3; } // 以len为模增加i private static int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0); } // 以len为模减少i private static int prevIndex(int i, int len) { return ((i - 1 >= 0) ? i - 1 : len - 1); } }