详解Thread_Local

17:Thread_Local

17.1、什么是Thread_Local

  • **ThreadLocal 实际上是一个容器,它可以存储线程本地的变量,并且保证每个线程之间互不干扰。**每个线程都拥有自己的 ThreadLocal 实例,它可以在其中存储数据,并且这些数据对其他线程是不可见的。ThreadLocal 的主要作用是提供线程内部的局部变量,可以将某个对象绑定到当前线程,然后在线程的任何地方都可以通过 ThreadLocal 获取到绑定的对象,而不需要通过方法参数传递。这样可以方便地在多个方法之间共享数据,同时又保证了线程安全。

17.2、demo

/**
 * ThreadLocal初认识
 * @author xxl
 * @since 2023/6/22
 */
public class Test47_ThreadLocal {
    public static ThreadLocal<Object> threadLocal = new ThreadLocal<>();
    public static void main(String[] args) {
        threadLocal.set("hello,world");

        ExecutorService executor =  Executors.newSingleThreadExecutor();
        executor.execute(()->{
            //打印主线线程存入的信息
            String info = (String) threadLocal.get();
            System.out.println(info);
        });

        String info = (String) threadLocal.get();
        System.out.println(info);
    }
}


//最终结果
//hello,world
//null

17.3、底层解密

  1. 所有的对象都是存在 JVM 的堆区中,可以被所有线程访问到。但是,ThreadLocal 的实现机制可以使得每个线程拥有自己独立的变量副本,其他线程无法直接访问到该副本。

  2. 在 ThreadLocal 的底层实现中,每个 ThreadLocal 实例都关联了一个 ThreadLocalMap 对象,其中存储了线程独立的变量副本。ThreadLocalMap 是一个自定义的 Map 实现,它的键是 ThreadLocal 实例,值是对应线程的变量副本。

  3. 当通过 ThreadLocal 的 set 方法设置变量时,实际上是将变量存储在当前线程的 ThreadLocalMap 中,键为 ThreadLocal 实例。而通过 get 方法获取变量时,会根据当前线程获取对应的 ThreadLocalMap,然后根据 ThreadLocal 实例作为键获取到相应的变量副本。

  4. 由于每个线程都有自己的 ThreadLocalMap,因此可以保证线程之间的数据互不干扰。不同线程之间访问的是各自的 ThreadLocalMap,而不会直接访问其他线程的变量副本。

  5. 这种机制在源码中的具体实现是通过线程的 ThreadLocal.ThreadLocalMap 字段实现的。每个线程在 Thread 类中都有一个名为 threadLocals 的字段,它是一个 ThreadLocal.ThreadLocalMap 类型的实例。通过该字段,可以实现线程级别的变量隔离。

  6. ThreadLocal 的设计思路是基于线程封闭性的原则,即通过将变量与线程绑定,避免了多线程并发访问共享数据的问题。这样可以简化代码的编写,提高程序的并发性和线程安全性。

  7. 需要注意的是,虽然每个线程独立拥有自己的变量副本,但并不意味着变量副本的生命周期与线程完全一致。当线程结束时,如果没有及时清理 ThreadLocalMap 中的变量副本,可能会导致内存泄漏问题。因此,在使用 ThreadLocal 时要注意适时清理和释放资源,避免潜在的问题。

结合代码

1.set方法

public void set(T value) {
        set(Thread.currentThread(), value);
    }

private void set(Thread t, T value) {
    	//ThreadLocal静态内部类,访问控制为default,只有同包下可以访问
        ThreadLocalMap map = getMap(t);
    	//这一步是判断map是否合法
        if (map == ThreadLocalMap.NOT_SUPPORTED) {
            throw new UnsupportedOperationException();
        }
        if (map != null) {
            //当前Thread_Local对象为键
            map.set(this, value);
        } else {
            createMap(t, value);
        }
    }
//每个线程都有一个threadLocals也就是ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
2.get方法
public T get() {
        return get(Thread.currentThread());
    }

private T get(Thread t) {
   		//通过当前线程获取绑定的Map
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            if (map == ThreadLocalMap.NOT_SUPPORTED) {
                return initialValue();
            } else {
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    T result = (T) e.value;
                    return result;
                }
            }
        }
        return setInitialValue(t);
    }

总结:以上存储过程没有详细,因为跟map差不多,有兴趣可以看看其实Thread_Local有点像session一样,实际作用就是相当于一个简易缓存

17.4、常见应用场景

1)以前前后端不分离存储用户信息大概率放在session中,分离之后要么存在缓存或者redis。现在多了一种方式:具体就是在spring boot使用拦截器获取token唯一的id查询出用户信息存在Thread_Local中,在拦截器afterCompletion方法中remove掉做到不泄露内存
2)数据库连接管理:在使用数据库连接池时,每个线程需要获取自己独立的数据库连接。可以使用 ThreadLocal 来管理线程的数据库连接,确保每个线程获取到的是自己的连接,避免了线程安全问题。

。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值