可以使用 java.lang.ThreadLocal 类来实现线程本地存储功能。

    ThreadLocal 从理论上讲并不是用来解决多线程并发问题的,因为根本不存在多线程竞争。

    在一些场景 (尤其是使用线程池) 下,由于 ThreadLocal.ThreadLocalMap 的底层数据结构导致 ThreadLocal 有内存泄漏的情况,应该尽可能在每次使用 ThreadLocal 后手动调用 remove(),以避免出现 ThreadLocal 经典的内存泄漏甚至是造成自身业务混乱的风险。

    对于以下代码,thread1 中设置 threadLocal 为 1,而 thread2 设置 threadLocal 为 2。过了一段时间之后,thread1 读取 threadLocal 依然是 1,不受 thread2 的影响。

    1. public class ThreadLocalExample {
    2. public static void main(String[] args) {
    3. ThreadPoolExecutor executor = new ThreadPoolExecutor(
    4. 5, 10, 1, TimeUnit.SECONDS,
    5. new ArrayBlockingQueue<>(100), new ThreadPoolExecutor.CallerRunsPolicy());
    6. ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
    7. executor.execute(() ->{
    8. threadLocal.set(1);
    9. try {
    10. Thread.sleep(1000);
    11. } catch (InterruptedException e) {
    12. e.printStackTrace();
    13. }
    14. System.out.println(threadLocal.get());
    15. threadLocal.remove();
    16. });
    17. executor.execute(() ->{
    18. threadLocal.set(2);
    19. threadLocal.remove();
    20. });
    21. executor.shutdown();
    22. }
    23. }
    1. public class ThreadLocalExample {
    2. public static void main(String[] args) {
    3. ThreadPoolExecutor executor = new ThreadPoolExecutor(
    4. 5, 10, 1, TimeUnit.SECONDS,
    5. new ArrayBlockingQueue<>(100), new ThreadPoolExecutor.CallerRunsPolicy());
    6. ThreadLocal<Integer> threadLocal1 = new ThreadLocal<>();
    7. ThreadLocal<Integer> threadLocal2 = new ThreadLocal<>();
    8. executor.execute(() ->{
    9. threadLocal1.set(1);
    10. threadLocal2.set(1);
    11. threadLocal1.remove();
    12. threadLocal2.remove();
    13. });
    14. executor.execute(() ->{
    15. threadLocal1.set(2);
    16. threadLocal2.set(2);
    17. threadLocal1.remove();
    18. threadLocal2.remove();
    19. });
    20. executor.shutdown();
    21. }
    22. }

    它所对应的底层结构图为:
    ThreadLocal - 图1

    每个 Thread 都有一个 ThreadLocal.ThreadLocalMap 对象。

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

    当调用一个 ThreadLocal 的 set(T value) 方法时,先得到当前线程的 ThreadLocalMap 对象,然后将 ThreadLocal->value 键值对插入到该 Map 中。

    1. public void set(T value) {
    2. Thread t = Thread.currentThread();
    3. ThreadLocalMap map = getMap(t);
    4. if (map != null)
    5. map.set(this, value);
    6. else
    7. createMap(t, value);
    8. }

    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. }