前言
当访问共享的可变数据时,通常需要使用同步。
如果仅在单线程内访问数据,就不需要同步。这种技术被称为线程封闭(Thread Confinement
)。
栈封闭
在栈封闭中,只能通过局部变量才能访问对象。
局部变量的固有属性之一就是封闭在执行线程中。它们位于执行线程的栈中,其他线程无法访问这个栈。
ThreadLocal 类
这个类能使线程中的某个值与保存值的对象关联起来。
ThreadLocal 提供了 set 和 get 等访问接口或方法,这些方法设置的值在每个线程中都存有一份独立的副本,因此 get 总是返回由当前线程在调用 set 时设置的最新值。
ThreadLocal 变量类似与全局变量,它能降低代码的可重用性,并在类之间引入隐含的耦合性,因此在使用时要格外小心。
实现原理
首先 ThreadLocal 是一个泛型类,保证可以接收任何类型的对象。
因为一个线程内可以存在多个 ThreadLocal 对象,所以其实是 ThreadLocal 内部维护了一个 Map ,这个 Map 不是直接使用的 HashMap ,而是 ThreadLocal 实现的一个叫做 ThreadLocalMap 的静态内部类。而我们使用的 get()、set() 方法其实都是调用了这个 ThreadLocalMap 类对应的 get()、set() 方法。例如下面的 set 方法:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
get方法:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
return (T)map.get(this);
// Maps are constructed lazily. if the map for this thread
// doesn't exist, create it, with this ThreadLocal and its
// initial value as its only entry.
T value = initialValue();
createMap(t, value);
return value;
}
createMap 方法:
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap 是个静态的内部类:
static class ThreadLocalMap {
........
}
ThreadLocalMap#set
ThreadLoca l中 put 函数最终调用了 ThreadLocalMap 中的 set 函数,跟进去看一看:
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
// 冲突了
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
在上述代码中如果 Entry 在存放过程中冲突了,调用 nextIndex
来处理,如下所示。是否还记得 hashmap 中对待冲突的处理?这里好像是另一种套路:只要 i 的数值小于 len,就加 1 取值,官方术语称为:线性探测法。
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}
参考资料
- 《Java并发编程实战》
- 深入理解 Java 之 ThreadLocal 工作原理