JUC
ThreadLocal 简介

面试题

  • ThreadLocal中ThreadLocalMap的数据结构和关系?
  • ThreadLocal的key是弱引用,这是为什么?
  • ThreadLocal内存泄露问题你知道吗?
  • ThreadLocal中最后为什么要加remove方法?

概述

This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).

ThreadLocal提供线程局部变量。这些变量与正常的变量不同,因为每一个线程在访问ThreadLocal实例的时候(通过其get或set方法)都有自己的、独立初始化的变量副本。ThreadLocal实例通常是类中的私有静态字段,使用它的目的是希望将状态(例如,用户ID或事务ID)与线程关联起来。

作用

实现每一个线程都有自己专属的本地变量副本

主要解决了让每个线程绑定自己的值,通过使用get()和set()方法,获取默认值或将其值更改为当前线程所存的副本的值从而避免了线程安全问题

API 介绍

变量和类型方法描述
Tget()返回当前线程的此线程局部变量副本中的值。
protected TinitialValue()返回此线程局部变量的当前线程的“初始值”。
voidremove()删除此线程局部变量的当前线程值。
voidset(T value)将此线程局部变量的当前线程副本设置为指定值。
static ThreadLocalwithInitial(Supplier<? extends S> supplier)创建一个线程局部变量。

案例

public class ThreadLocalDemo {
 
    // 创建 ThreadLocal 变量
    private static ThreadLocal<Integer> threadLocalCount = new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            return 0; // 初始值设置为 0
        }
    };
 
    public static void main(String[] args) {
        // 创建三个线程,每个线程都尝试修改 ThreadLocal 变量
        Thread thread1 = new Thread(new MyRunnable(), "Thread-1");
        Thread thread2 = new Thread(new MyRunnable(), "Thread-2");
        Thread thread3 = new Thread(new MyRunnable(), "Thread-3");
 
        // 启动线程
        thread1.start();
        thread2.start();
        thread3.start();
    }
 
    public static class MyRunnable implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                // 获取当前线程的 ThreadLocal 变量值,然后增加 1
                int count = threadLocalCount.get();
                count++;
                threadLocalCount.set(count);
 
                System.out.println(Thread.currentThread().getName() + ": " + threadLocalCount.get());
            }
            // 在最后清除 ThreadLocal 存储的变量,这是一个好习惯,特别是在使用线程池的情况下
            threadLocalCount.remove();
        }
    }
}

总结

  • 因为每个 Thread 内有自己的实例副本且该副本只有当前线程自己使用
  • 既然其他 ThreadLocal 不可访问,那就不存在多线程间共享问题
  • 统一设置初始值,但是每个线程对这个值得修改都是各自线程互相独立得