ThreadLocal
对象是线程的局部变量,每个线程都能在其中保存只属于自己的内容。对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。
结构体系大概如下:
ThreadLocal如何保证这些变量只被当前线程所访问的呢?
ThreadLocal.set
首先看一下ThreadLocal的set():
public class ThreadLocal<T> {
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
}
set时,首先获取当前线程对象,然后通过getMap获取线程的ThreadLocalMap,并将值设置ThreadLocalMap中,ThreadLocalMap可以理解为一个Map。map.set()如下:
static class ThreadLocalMap {
/**
* Set the value associated with key.
*
* @param key the thread local object
* @param value the value to be set
*/
private void set(ThreadLocal<?> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
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();
}
}
方法中循环了map中的元素,对map中的key和入参key比较,如果相同则赋新值,如果map中key存在null值则替换,否则重新生成一个map元素。
如果没有map,则执行createMap():
/**
* Create the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the map
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
createMap中构造了ThreadLocalMap:
/**
* Construct a new map initially containing (firstKey, firstValue).
* ThreadLocalMaps are constructed lazily, so we only create
* one when we have at least one entry to put in it.
*/
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
在这里,为Thread的threadLocals赋值。
ThreadLocal.get
而设置到ThreadLocal中的数据,实质上是写入了Thread的threadLocals属性。其中key为ThreadLocal当前对象,value就是我们需要的值。Thread的threadLocals保存了当前自己所在线程的所有“局部变量”,也就是一个ThreadLocal变量的集合。
在进行get操作时,自然就是将这个Map中的数据拿出来:
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
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方法也是先取得当前线程的ThreadLocalMap对象,然后,将自己传入作为getMap的参数,而getMap返回的则是当前线程的threadLocals属性:
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
即当前线程的threadLocals属性,也就是说,ThreadLocal类的getMap方法返回的是Thread类的threadLocals属性:
public class Thread implements Runnable {
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
因此,threadLocals是维护在Thread内部的,这意味着只要线程不退出,对象的引用就一直存在。
当线程退出时会进行一些清理工作,其中包括对ThreadLocalMap的清理:
/**
* This method is called by the system to give a Thread
* a chance to clean up before it actually exits.
*/
private void exit() {
if (group != null) {
group.threadTerminated(this);
group = null;
}
/* Aggressively null out all reference fields: see bug 4006245 */
target = null;
/* Speed the release of some of these resources */
threadLocals = null;
inheritableThreadLocals = null;
inheritedAccessControlContext = null;
blocker = null;
uncaughtExceptionHandler = null;
}
如果使用线程池的话,线程未必会退出,此时将一些比较大的对象放入ThreadLocal中(实际保存在线程持有的threadLocals Map中),可能会导致内存泄漏的问题。这时可以使用ThreadLocal.remove()进行移除:
/**
* Removes the current thread's value for this thread-local
* variable. If this thread-local variable is subsequently
* {@linkplain #get read} by the current thread, its value will be
* reinitialized by invoking its {@link #initialValue} method,
* unless its value is {@linkplain #set set} by the current thread
* in the interim. This may result in multiple invocations of the
* {@code initialValue} method in the current thread.
*
* @since 1.5
*/
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
实例演示
public class Test {
static ThreadLocal<String> threadLocal = new ThreadLocal<>();
static ThreadLocal<String> anotherThreadLocal = new ThreadLocal<>();
static AtomicInteger atomicInteger = new AtomicInteger(0);
static class ThreadDemo implements Runnable {
@Override
public void run() {
if (threadLocal.get() == null) {
threadLocal.set("线程:" + atomicInteger.addAndGet(1));
}
if (anotherThreadLocal.get() == null) {
anotherThreadLocal.set("anotherThreadLocal:" + atomicInteger.get());
}
System.out.println(threadLocal.get());
System.out.println(anotherThreadLocal.get());
}
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
executorService.execute(new ThreadDemo());
}
}
}
表明,一个ThreadLocal可以供多个线程共享,但是每个线程只能拿到属于自己的那一份存储。每一个线程的threadLocals的大小可以为多个,每一个ThreadLocal对象都可以会作为该map的key存在。