该类主要用于不同线程存储自己的线程本地变量。本文先通过一个示例简单介绍该类的使用方法,然后从ThreadLocal类的初始化、存储结构、增删数据和hash值计算等几个方面,分析对应源码。采用的版本为jdk1.8。
ThreadLocal-使用方法ThreadLocal对象可以在多个线程中被使用,通过set()方法设置线程本地变量,通过get()方法获取设置的线程本地变量。我们先通过一个示例简单了解下使用方法:
public static void main(String[] args){ ThreadLocal<String> threadLocal = new ThreadLocal<>(); // 线程1 new Thread(()->{ // 查看是否有初始值 System.out.println("线程1的初始值:"+threadLocal.get()); // 设置线程1的值 threadLocal.set("V1"); // 输出 System.out.println("线程1的值:"+threadLocal.get()); // 等待一段时间,等线程2设置值后再查看线程1的值 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程1的值:"+threadLocal.get()); }).start(); // 线程2 new Thread(()->{ // 等待线程1设置初始值 try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } // 查看线程2的初始值 System.out.println("线程2的值:"+threadLocal.get()); // 设置线程2的值 threadLocal.set("V2"); // 查看线程2的值 System.out.println("线程2的值:"+threadLocal.get()); }).start(); }由于threadlocal设置的值是在每个线程中都有一个副本的,线程之间不会互相影响。代码运行的结果如下所示:
线程1的初始值:null 线程1的值:V1 线程2的值:null 线程2的值:V2 线程1的值:V1 ThreadLocal-初始化ThreadLocal类只有一个无参的构造方法,如下所示:
/** * Creates a thread local variable. * @see #withInitial(java.util.function.Supplier) */ public ThreadLocal() { }但其实还有一个带参数的构造方法,不过是它的子类。ThreadLocal中定义了一个内部类SuppliedThreadLocal,为继承自ThreadLocal类的子类。可以通过该类进行给定初始值的初始化,其定义如下:
static final class SuppliedThreadLocal<T> extends ThreadLocal<T> { private final Supplier<? extends T> supplier; SuppliedThreadLocal(Supplier<? extends T> supplier) { this.supplier = Objects.requireNonNull(supplier); } @Override protected T initialValue() { return supplier.get(); } }通过TheadLocal threadLocal = Thread.withInitial(supplier);这样的语句可以进行给定初始值的初始化。在某个线程第一次调用get()方法时,会执行initialValue()方法设置线程变量为传入supplier中的值。
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) { return new SuppliedThreadLocal<>(supplier); } ThreadLocal-存储结构在jdk1.8版本中,使用的是TheadLocalMap这一个容器存储线程本地变量。
该容器的设计思想和HashMap有很多共同之处。比如:内部定义了Entry节点存储键值对(使用ThreadLocal对象作为键);使用一个数组存储entry节点;设定一个阈值,超过阈值时进行扩容;通过键的hash值与数组长度进行&操作确定下标索引等。但也有很多不同之处,具体我们在后续介绍ThreadLocalMap类时再详细分析。
ThreadLocal类提供了get(),set()和remove()方法来操作当前线程的threadlocal变量副本。底层则是基于ThreadLocalMap容器来实现数据操作。
不过要注意的是:ThreadLocal中并没有ThreadLocalMap的成员变量,ThreadLocalMap对象是Thread类中的一个成员,所以需要通过通过当前线程的Thread对象去获取该容器。
每一个线程Thread对象都会有一个map容器,该容器会随着线程的终结而回收。
设置线程本地变量的方法。