ThreadLocal线程局部变量

我们创建一个全局变量时,经常使用pulic static,但有些情况下我们需要一个线程局部变量,即只属于一个线程的“全局变量”。这当然可以通过读取线程号加哈希表解决,但更好的方式是使用JDK提供的线程局部变量ThreadLocal

此外我们要知道,线程局部变量其实在服务端开发中非常常用。Tomcat使用多线程的并发模型处理请求,如果我们希望在整个请求的作用域下保存一些线程内“全局”的数据,这些数据其实就可以用线程局部变量实现;另外在调用链日志记录领域,MDC也是通过线程局部变量实现的。

ThreadLocal的使用

MyThread.java

package com.gacfox.demo;

import java.util.Random;

public class MyThread implements Runnable {
    @Override
    public void run() {
        Random random = new Random();
        Main.threadLocal.set(random.nextInt());
        System.out.println(Main.threadLocal.get());
    }
}

Main.java

package com.gacfox.demo;

public class Main {
    public static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) {
        Thread myThread1 = new Thread(new MyThread());
        Thread myThread2 = new Thread(new MyThread());
        Thread myThread3 = new Thread(new MyThread());
        Thread myThread4 = new Thread(new MyThread());
        Thread myThread5 = new Thread(new MyThread());

        myThread1.start();
        myThread2.start();
        myThread3.start();
        myThread4.start();
        myThread5.start();
    }
}

我们创建了5个线程,他们都读写了public static ThreadLocal<Integer> threadLocal,但显然他们读写的内容都是线程隔离的。

运行结果:

带默认值的线程局部变量

ThreadLocal默认是没有值的,不set时直接get返回的是null。我们可以继承ThreadLocal,覆盖一些方法,编写一个我们自己的线程局部变量类。

public class MyThreadLocal extends ThreadLocal<Integer> {
    @Override
    protected Integer initialValue() {
        return 1;
    }
}

可继承的线程局部变量

JDK还提供了InheritableThreadLocal,子线程可以继承父线程的线程局部变量。不过当然,这两个值是隔离的,也就是说线程局部变量是从父线程复制了一份给子线程。

但是如果线程局部变量的值是一个引用就要小心了,子线程如果直接使用这个引用,那么它的改动会影响到父线程,这种用法下就要考虑线程安全问题了。如果子线程覆盖了这个引用,那就没什么问题了,这两个引用是隔离的,其指向的对象也完全隔离了。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。