五、线程本地ThreadLocal

时间:2021-08-12 22:21:12

一、线程私有

在多线程情况下,对于一个共享的数据可能会产生线程安全问题。最简单的解决办法就是堆访问共享数据的时候加锁,但我们知道加锁是很影响效率的,尤其是像数据库连接这样耗费资源较多的情况下,加锁就意味着你的程序无法提供高效的服务。

那么我们会这样思考,是否每个线程能够自己独有一份数据呢?这样线程之间不会相互影响,也就不存在数据共享的线程安全问题了,ThreadLocal类便是对于这样的需求的具体实现类。

JDK文档:http://tool.oschina.net/uploads/apidocs/jdk-zh/java/lang/ThreadLocal.html

二、ThreadLocal类

ThreadLocal类直接继承与Object类,它存在于java.lang包下。

该类使得每个线程拥有自己的局部变量,该变量独立于变量的初始化副本,也就是说每个线程其实是持有着一份拷贝的副本,并不直接使用初始化数据。ThreadLocal的实例通常是private static 字段描述,线程私有的数据将在线程销毁以后随之清除。

构造方法:

ThreadLocal() // 创建一个线程本地变量。

方法摘要:

 T    get() // 返回此线程局部变量的当前线程副本中的值。
protected  T    initialValue() // 返回此线程局部变量的当前线程的“初始值”。
 void    remove() // 移除此线程局部变量当前线程的值。
 void    set(T value) // 将此线程局部变量的当前线程副本中的值设置为指定值

三、示例

public class ThreadLocalTest {
    private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            // 初始化值为1
            return 1;
        }
    };

    public static void main(String[] args) {
        System.out.println("主线程获取初始值副本:" + threadLocal.get());
        threadLocal.set(2); // 设置主线程副本值
        System.out.println("主线程获取副本新的值:" + threadLocal.get());
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("另起一个线程获取副本值:" + threadLocal.get());;
            }
        }).start();
        threadLocal.remove(); // 移除主线程的副本
        System.out.println("主线程重新获取副本的值:" + threadLocal.get());
    }
}

控制台打印:

主线程获取初始值副本:1
主线程获取副本新的值:2
另起一个线程获取副本值:1
主线程重新获取副本的值:1

从示例中我们看到:

主线程拷贝了一份初始值1,然后设置了初始值2,另起一个线程又从初始值中拷贝了1,与主线程设置的值无关。

当主线程移除了副本以后,再次调用get()方法则将再次从初始值中拷贝。

注意:如果没有重写initialValue,那么get()方法将取到null值。