ThreadLocal
这个类提供“thread-local”的一些变量。这些变量与普通变量不同,每个的线程可以通过各自的get或set方法对这些的独立的初始化的变量的副本(copy)进行操作。ThreadLocal
的实例在类里面通常是一个私有静态字段(private static field),通过这个方式来将状态与线程相关联(比如,user ID或Transcation ID)。
下面这个代码示例中,ThreadId
类生成每个线程的本地唯一标识符。线程的id在第一次调用ThreadId.get()
时分配,并在后续调用中保持不变。
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadId {
// Atomic integer containing the next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId =
new ThreadLocal<Integer>() {
@Override protected Integer initialValue() {
return nextId.getAndIncrement();
}
};
// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
}
也就是说,只要线程处于alive状态并且ThreadLocal
的实例可访问,那么每个线程都持有对它自己的“thread-local”变量的副本的隐式引用。若线程消失,其“thread-local”实例的所有变量的副本都会垃圾回收(除非存在对这些变量的副本的其他引用)。
public class ThreadLocal<T> {
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
...
}
get()
方法返回当前线程“thread-local”变量的副本中的值。如果变量没有当前线程的值,则优先初始化的方式是调用initialValue
方法得到初始值。
public class ThreadLocal<T> {
/**
* 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;
}
...
}
set()的变体用于建立initialValue,通常会override这个set()方法。
ThreadLocalMap
是一个自定义的哈希的map,仅适用维护线程local值。不会在类ThreadLocal
外部导出任何操作。 该类是package private的,允许在类Thread
中声明字段。
public
class Thread implements Runnable {
//线程相关的`ThreadLocal`的值。这个map由类`ThreadLocal`维护。
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
...
}
针对处理非常多且长期存在的用法哈希表的entries对keys使用弱引用(WeakReference
)。但是,由于不使用引用队列,因此仅当表开始空间不足,才能保证删除过时的entries。
ThreadLocalMap的Entry
public class ThreadLocal<T> {
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
...
}
...
}
ThreadLocalMap的table
public class ThreadLocal<T> {
static class ThreadLocalMap {
/** The table, resized as necessary. table.length MUST always be a power of two.*/
private Entry[] table;
...
}
...
}
哈希的map的entries继承了WeakReference
,使用其主要的ref字段(指T referent
)作为key(始终是一个ThreadLocal
对象)。
注意,空键(即entry.get() == null
)表示不再引用该键,因此可以从表中删除这个entry
。这类entry
被称为过时的entry
(stale entries)。
类InheritableThreadLocal
继承了类ThreadLocal
,可提供从父线程到子线程的值的继承。
当创建子线程时,父线程所具有的全部的可以继承的“thread-local”的变量的值可被子线程接收,作为子线程的初始值。
通常,子线程的值和父线程的值是相同的,但是可以通过重写类InheritableThreadLocal
中的方法chileValue()方法,使子线程的值是父线程值的任意自定义函数的映射值。
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
protected T childValue(T parentValue) {
return parentValue;
}
...
}
当创建子线程时,变量中维护的每个线程的属性(比如,user ID或Transcation ID)必须传输到子线程。注意,可继承的“thread-local”变量优先级高于普通“thread-local”变量。