学习 准备 尝试 谨慎小心

0%

ThreadLocal 理解

线程成员 threadLocals

线程类Thread内部有一个变量:threadLocals

它是 ThreadLocal.ThreadLocalMap 对象

1
...
2
public
3
class Thread implements Runnable {
4
    ...
5
	// line 179
6
	/* ThreadLocal values pertaining to this thread. This map is maintained
7
     * by the ThreadLocal class. */
8
    ThreadLocal.ThreadLocalMap threadLocals = null;
9
    ...
10
}

类ThreadLocalMap

ThreadLocalMapThreadLocal 的内部类

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

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

也就是说,Thread 有一个类似 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
	// 获取当前线程
13
    Thread t = Thread.currentThread();
14
    // 获取线程内部 ThreadLocalMap 对象
15
    ThreadLocalMap map = getMap(t);
16
    if (map != null)
17
        map.set(this, value);
18
    else// 给线程threadLocals变量new一个ThreadLocalMap对象,并添加第一个键值对
19
        createMap(t, value);
20
}
21
......
22
// line 225
23
/**
24
 * Get the map associated with a ThreadLocal. Overridden in
25
 * InheritableThreadLocal.
26
 *
27
 * @param  t the current thread
28
 * @return the map
29
 */
30
ThreadLocalMap getMap(Thread t) {
31
    return t.threadLocals;
32
}
33
34
/**
35
 * Create the map associated with a ThreadLocal. Overridden in
36
 * InheritableThreadLocal.
37
 *
38
 * @param t the current thread
39
 * @param firstValue value for the initial entry of the map
40
 */
41
void createMap(Thread t, T firstValue) {
42
    t.threadLocals = new ThreadLocalMap(this, firstValue);
43
}

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
}

小结

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

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

正因为 ThreadLocal 的线程隔离特性,使他的应用场景相对来说更为特殊一些。

当某些数据是 以线程为作用域并且不同线程具有不同的数据副本 的时候,就可以考虑采用ThreadLocal。

比如:在多数据源切换时,就使用了 ThreadLocal 来设置当前使用的数据源