ThreadLocal

ThreadLocal 很容易让人望文生义,想当然地认为是一个 “本地线程”。其实,ThreadLocal 并不是一个 Thread,而是 Thread 的局部变量。当使用 ThreadLocal 维护变量时,ThreadLocal 为每个使用该变量的线程提供独立的变量副本,因此每一个线程都可以独立地改变自己的副本,而不会影响其他线程对应的副本。

ThreadLocal 是由 JDK 包提供的线程本地变量,如果你创建了一个 ThreadLocal 变量,那么访问这个变量的每个线程都会有这个变量的一个本地副本。当多个线程操作这个变量时,实际操作的是自己本地内存里面的变量,从而避免了线程安全问题。

Java 提供了 3 个用于操作 ThreadLocal 变量的方法,即 set() 方法、get() 方法和 remove() 方法。

  • set() 方法负责设置 ThreadLocal 变量,设置成功后,该变量只能够被当前线程访问,其他线程不可直接访问、操作该变量。set() 方法可以设置任何类型的值,无论是 String 类型,Integer 类型,Object 类型等,原因在于 set() 方法的 JDK 源码实现是基于泛型的实现。

  • get() 方法负责获取 ThreadLocal 变量的值,get() 方法没有任何入参,直接调用即可获取。

  • remove() 方法负责清除 ThreadLocal 变量,清除成功后,该变量中没有值。remove() 方法同 get() 方法一样,是没有任何入参的,因为 ThreadLocal 变量中只能存储一个值,那么 remove() 方法会直接清除这个变量值。

【例21.4】用户线程等待守护线程(实例位置:资源包\TM\sl\21\4)

创建一个全局的静态 ThreadLocal 变量,存储 String 类型变量;创建两个线程,分别为 threadOne 和 threadTwo;threadOne 线程调用 set() 方法进行设置,设置完成后休眠 5000 毫秒,苏醒后调用 get() 方法进行输出;threadTwo 线程调用 set() 方法进行设置,设置完成后直接调用 get() 方法进行输出,输出完成后调用 remove() 方法,并输出 remove() 方法被调用完毕的语句;开启线程 threadOne 和 threadTwo;执行程序,并观察输出结果。代码如下:

public class Demo {
    static ThreadLocal<String> local = new ThreadLocal<>();
    public static void main(String[] args) {
        Thread threadOne = new Thread(new Runnable() {
            @Override
            public void run() {
                local.set("threadOne's local value");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread threadTwo = new Thread(new Runnable() {
            @Override
            public void run() {
                local.set("threadTwo's local value");
                System.out.println(local.get());
                local.remove();
                System.out.println("local变量已经执行 remove() 方法。");
            }
        });
        threadOne.start();
        threadTwo.start();
    }
}

运行结果如图21.6所示。

image 2024 03 06 13 11 04 701
Figure 1. 图21.6 运行结果

在成功设置 threadOne 线程后,threadOne 线程会进入 5000 毫秒的休眠状态,此时由于只有 threadTwo 线程调用了 remove() 方法,因此将不会影响 threadOne 线程调用 get() 方法进行输出。这体现了 ThreadLocal 变量的显著特性:线程独有数据,其他线程不可侵犯此线程的数据。