Java面试知识点(八十七)Java中的ThreadLocal

一、定义

  1. ThreadLocal 是线程的局部变量, 是每一个线程所单独持有的,其他线程不能对其进行访问, 通常是类中的 private static 字段,是对该字段初始值的一个拷贝,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联

  2. Synchronized 用于线程间的数据共享,而 ThreadLocal 则用于线程间的数据隔离。

  3. 当使用 ThreadLocal 维护变量的时候 为每一个使用该变量的线程提供一个独立的变量副本,即每个线程内部都会有一个该变量,这样同时多个线程访问该变量并不会彼此相互影响,因此他们使用的都是自己从内存中拷贝过来的变量的副本, 这样就不存在线程安全问题,也不会影响程序的执行性能。

  4. ThreadLocal 是线程 Thread中属性 threadLocals 的管理者。

  5. 但是要注意,虽然 ThreadLocal 能够解决上面说的问题,但是由于在每个线程中都创建了副本,所以要考虑它对资源的消耗,比如内存的占用会比不使用 ThreadLocal 要大。


二、原理和方法

1.ThreadLocal 方法

ThreadLocal 可以存储任何类型的变量对象, get 返回的是一个 Object 对象,但是我们可以通过泛型来制定存储对象的类型。

  • public T get() { } // 用来获取ThreadLocal在当前线程中保存的变量副本
  • public void set(T value) { } //set()用来设置当前线程中变量的副本
  • public void remove() { } //remove()用来移除当前线程中变量的副本
  • protected T initialValue() { } //initialValue()是一个protected方法,一般是用来在使用时进行重写的,该方法返回该线程局部变量的初始值。

2.使用过程

Thread 在内部是通过 ThreadLocalMap 来维护 ThreadLocal 变量表, 在 Thread 类中有一个 threadLocals 变量,是 ThreadLocalMap 类型的,它就是为每一个线程来存储自身的 ThreadLocal 变量的, ThreadLocalMap 是 ThreadLocal 类的一个内部类,这个 Map 里面的最小的存储单位是一个 Entry, 它使用 ThreadLocal 作为 key, 变量作为 value,这是因为在每一个线程里面,可能存在着多个 ThreadLocal 变量

3.原理

  • ThreadLocal,连接 ThreadLocalMap 和 Thread。来处理 Thread 的 TheadLocalMap 属性,包括 init 初始化属性赋值、get 对应的变量,set 设置变量等。通过当前线程,获取线程上的 ThreadLocalMap 属性,对数据进行 get、set 等操作。

  • ThreadLocalMap,用来存储数据,采用类似 hashmap 机制,存储了以 threadLocal 为 key,需要隔离的数据为 value 的 Entry 键值对数组结构。

  • ThreadLocal,有个 ThreadLocalMap 类型的属性,存储的数据就放在这儿。

4.举例

在这里插入图片描述

  • 每个人都一张银行卡
  • 每个人每张卡都有一定的余额。
  • 每个人获取银行卡余额都必须通过该银行的管理系统。
  • 每个人都只能获取自己卡持有的余额信息,他人的不可访问。

在这里插入图片描述
映射到我们要说的 ThreadLocal

  • card 类似于 Thread
  • card 余额属性,卡号属性等类似于 Treadlocal 内部属性集合 threadLocals
  • cardManager 类似于 ThreadLocal 管理类

三、线程隔离

核心就是ThreadLocalMap类。

threadLocalMap 是 ThreadLocal 类的一个静态内部类,它实现了键值对的设置和获取(对比 Map 对象来理解),每个线程中都有一个独立的 ThreadLocalMap 副本,它所存储的值,只能被当前线程读取和修改。ThreadLocal 类通过操作每一个线程特有的 ThreadLocalMap 副本,从而实现了变量访问在不同线程中的隔离。因为每个线程的变量都是自己特有的,完全不会有并发错误。还有一点就是,ThreadLocalMap 存储的键值对中的键是 this 对象指向的 ThreadLocal 对象,而值就是你所设置的对象了。


四、与同步机制的比较

  1. Synchronized 用于线程间的数据共享,而 ThreadLocal 则用于线程间的数据隔离。

  2. 在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。

  3. ThreadLocal 则从另一个角度来解决多线程的并发访问。ThreadLocal 会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal 提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进 ThreadLocal。

  4. 概括起来说,对于多线程资源共享的问题,同步机制采用了 “以时间换空间” 的方式,而 ThreadLocal 采用了 “以空间换时间” 的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。


五、应用

1.代码示例

package test.threads.threadlocal;

public class ThreadLocalTest {
    public static void main(String[] args) throws InterruptedException {
        final ThreadLocalTest test = new ThreadLocalTest();

        test.set();
        System.out.println(test.getLong());
        System.out.println(test.getString());
        // 在这里新建了一个线程
        Thread thread1 = new Thread() {
            public void run() {
                /**
                 * 当这里调用了set方法,进一步调用了ThreadLocal的set方法是,
                 * 会将ThreadLocal变量存储到该线程的ThreadLocalMap类型的成员变量threadLocals中,
                 * 注意的是这个threadLocals变量是Thread线程的一个变量,而不是ThreadLocal类的变量。
                 * */
                test.set();
                System.out.println(test.getLong());
                System.out.println(test.getString());
            };
        };
        thread1.start();
        thread1.join();

        System.out.println(test.getLong());
        System.out.println(test.getString());
    }

    ThreadLocal<Long> longLocal = new ThreadLocal<Long>();
    ThreadLocal<String> stringLocal = new ThreadLocal<String>();

    /**
     * 这里是获取当前的线程,并把变量存到当前线程的threadlocals中
     * */
    public void set() {
        longLocal.set(Thread.currentThread().getId());
        stringLocal.set(Thread.currentThread().getName());
    }

    public long getLong() {
        return longLocal.get();
    }

    public String getString() {
        return stringLocal.get();
    }

}

运行结果:

1
main
12
Thread-0
1
main

2.应用场景

最常见的 ThreadLocal 使用场景为
用来解决 数据库连接、 Session 管理

发布了147 篇原创文章 · 获赞 835 · 访问量 27万+

猜你喜欢

转载自blog.csdn.net/qq_33945246/article/details/102893961