学习 准备 尝试 谨慎小心

0%

ThreadLocal 理解

Thread 变量 threadLocals

线程类Thread内部有一个名为threadLocalsThreadLocal.ThreadLocalMap 变量,它定义如下:

1
/* ThreadLocal values pertaining to this thread. This map is maintained
2
     * by the ThreadLocal class. */
3
    ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocal.ThreadLocalMap

ThreadLocal.ThreadLocalMapThreadLocal 的内部类

1
/**
2
 * ThreadLocalMap is a customized hash map suitable only for
3
 * maintaining thread local values. No operations are exported
4
 * outside of the ThreadLocal class. The class is package private to
5
 * allow declaration of fields in class Thread.  To help deal with
6
 * very large and long-lived usages, the hash table entries use
7
 * WeakReferences for keys. However, since reference queues are not
8
 * used, stale entries are guaranteed to be removed only when
9
 * the table starts running out of space.
10
 */
11
static class ThreadLocalMap {
12
    // ...
13
}

看注释的第一句话 ThreadLocalMap is a customized hash map suitable only for maintaining thread local values. 意思是 ThreadLocalMap 是一个自定义 HashMap,只适用于线程局部变量。完全可以把它看做是一个 HashMap。

ThreadLocal

1
/**
2
 * This class provides thread-local variables.  These variables differ from
3
 * their normal counterparts in that each thread that accesses one (via its
4
 * {@code get} or {@code set} method) has its own, independently initialized
5
 * copy of the variable.  {@code ThreadLocal} instances are typically private
6
 * static fields in classes that wish to associate state with a thread (e.g.,
7
 * a user ID or Transaction ID).
8
 *
9
 * <p>For example, the class below generates unique identifiers local to each
10
 * thread.
11
 * A thread's id is assigned the first time it invokes {@code ThreadId.get()}
12
 * and remains unchanged on subsequent calls.
13
 * 
14
 * ...
15
 *
16
 * @author  Josh Bloch and Doug Lea
17
 * @since   1.2
18
 */
19
public class ThreadLocal<T> {
20
    //...
21
}

大致意思就是,ThreadLocal 提供线程局部的变量,这些变量在每个线程中都有不同的副本,通过get和set方法就可以得到或设置它们。

ThreadLocal的结构如下:
类`ThreadLocal`的结构

重要的方法只有两个:set 和 get

ThreadLocal.set(value)

ThreadLocal.set(value)Thread 内部的 threadLocals 变量添加值,它的实现如下:

1
// line 189
2
/**
3
 * Sets the current thread's copy of this thread-local variable
4
 * to the specified value.  Most subclasses will have no need to
5
 * override this method, relying solely on the {@link #initialValue}
6
 * method to set the values of thread-locals.
7
 *
8
 * @param value the value to be stored in the current thread's copy of
9
 *        this thread-local.
10
 */
11
public void set(T value) {
12
    Thread t = Thread.currentThread();
13
    // 获取线程内部 ThreadLocalMap 对象
14
    ThreadLocalMap map = getMap(t);
15
    if (map != null)
16
        map.set(this, value);
17
    else// 给线程threadLocals变量new一个ThreadLocalMap对象,并添加第一个键值对
18
        createMap(t, value);
19
}
20
......
21
// line 225
22
/**
23
 * Get the map associated with a ThreadLocal. Overridden in
24
 * InheritableThreadLocal.
25
 *
26
 * @param  t the current thread
27
 * @return the map
28
 */
29
ThreadLocalMap getMap(Thread t) {
30
    return t.threadLocals;
31
}
32
33
/**
34
 * Create the map associated with a ThreadLocal. Overridden in
35
 * InheritableThreadLocal.
36
 *
37
 * @param t the current thread
38
 * @param firstValue value for the initial entry of the map
39
 */
40
void createMap(Thread t, T firstValue) {
41
    t.threadLocals = new ThreadLocalMap(this, firstValue);
42
}

ThreadLocal.get()

1
public T get() {
2
    Thread t = Thread.currentThread();
3
    ThreadLocalMap map = getMap(t);
4
    if (map != null) {
5
        ThreadLocalMap.Entry e = map.getEntry(this);
6
        if (e != null) {
7
            @SuppressWarnings("unchecked")
8
            T result = (T)e.value;
9
            return result;
10
        }
11
    }
12
    return setInitialValue();
13
}

小结

ThreadLocalSynchronized都是为了解决多线程中相同变量的访问冲突问题,不同的是:

  • Synchronized 是通过线程等待,牺牲时间来解决访问冲突
  • ThreadLocal 是通过每个线程单独一份存储空间,牺牲空间来解决冲突,并且相比于SynchronizedThreadLocal 具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问到想要的值。

正因为ThreadLocal的线程隔离特性,使他的应用场景相对来说更为特殊一些。在android中Looper、ActivityThread以及AMS中都用到了ThreadLocal。当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用ThreadLocal。