首页 > 编程语言 >【Java】深入解析ThreadLocal——Java并发编程的秘密武器

【Java】深入解析ThreadLocal——Java并发编程的秘密武器

时间:2024-09-21 14:22:59浏览次数:17  
标签:Java Thread get 秘密武器 ThreadLocal 线程 数据 public

        ThreadLocal 被称为线程局部变量,用于在线程中保存数据。由于在 ThreadLocal中保存的数据仅属于当前线程,所以该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量。

        ThreadLocal 用于在同一个线程间,在不同的类和方法之间共享数据的的场景,也可以用于在不同线程间隔离数据的场景。

        ThreadLocal利用 Thread 中的 ThreadLocalMap 来进行数据存储。

一、常用方法

1. 存储数据至当前线程的 ThreadLocalMap:public void set(T value)

2. 从当前线程的 ThreadLocalMap 中获取数据:public T get()

3. 从当前线程的 ThreadLocalMap 中删除数据:public void remove()

        在线程池的线程复用场景中,线程执行完毕时一定要调用remove(),避免在线程被重新放入线程池中时被本地变量的旧状态仍然被保存。

        ThreadLocal的get()方法、set()方法和remove()方法,其实最终操作的都是 ThreadLocalmap 类中的数据。

二、ThreadLocalMap内部结构

        ThreadLocalMap 内部数据结构是一个 Entry 类型的数组。每个 Entry 对象的 key为 ThreadLocal对象,value 为存储的数据

三、为什么用ThreadLocal做key

        如果在应用中,一个线程中只使用了一个ThreadLocal 对象,那么使用 Thread做 key 也是可以的,代表每个 Thread 线程对应一个 value 。

        但是,在实际应用程序开发过程中,一个线程中很有可能不只使用了一个ThreadLocal 对象。这时使用 Thread 做 key 就会产生混淆

        所以,不能使用 Thread 做 key ,而应该改成用 ThreadLocal 对象做 key,这样才能通过具体 ThreadLocal对象的get()方法,获取到当前线程的 ThreadLocalMap,然后进一步获取到对应的Entry。

public class Test {
    private static ThreadLocal<String> namethreadLocal = new ThreadLocal();
    private static ThreadLocal<String> idthreadLocal = new ThreadLocal();

    public static void main(String[] args) {

        Thread t1 = new Thread(()->{
            try{
                // 保存数据
                namethreadLocal.set("马超");
                idthreadLocal.set("001");

                // 调用方法,查看获取数据
                dosth();
            }finally {
                namethreadLocal.remove();
                idthreadLocal.remove();
            }
        });

        Thread t2 = new Thread(()->{
            try{
                namethreadLocal.set("马云");
                idthreadLocal.set("008");
                dosth();
            }finally {
                namethreadLocal.remove();
                idthreadLocal.remove();
            }
        });

        t1.start();
        t2.start();

    }
    public static void dosth(){
        // 通过threadLocal,获取当前线程中的数据
        String name = namethreadLocal.get();
        System.out.println(Thread.currentThread().getName()+"-dosth:"+name);

        String id = idthreadLocal.get();
        System.out.println(Thread.currentThread().getName()+"-dosth:"+id);

        // 线程继续调用方法,获取当前线程的数据
        show();
    }

    public static void show(){
        // 通过threadLocal,获取当前线程中的数据
        String name = namethreadLocal.get();
        System.out.println(Thread.currentThread().getName()+"-show:"+name);

        String id = idthreadLocal.get();
        System.out.println(Thread.currentThread().getName()+"-show:"+id);
    }

}

四、父子线程如何共享数据

        在实际工作中,有可能需要在父子线程共享数据的。即:在父线程中往 ThreadLocal 设置了值,在子线程中能够获取到。

        使用JDK自带的InheritableThreadLocal类,该类继承了ThreadLocal

// 父子线程传递数据
public class Test{
    public static void main(String[] args) {

        // ThreadLocal threadLocal = new ThreadLocal();  // 子线程不能获取父线程数据
        InheritableThreadLocal threadLocal = new InheritableThreadLocal(); // 子线程可以获取父线程数据

        threadLocal.set("天王盖地虎");
        System.out.println(Thread.currentThread().getName()+"主线程:"+threadLocal.get());

        Thread t = new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"子线程:"+threadLocal.get());
        });
        t.start();
    }
}

五、ThreadLocal应用场景

1. 线程数据隔离

        ThreadLocal 的主要价值在于线程隔离,ThreadLocal 中的数据只属于当前线程,该数据对别的线程是不可见的,起到隔离作用。这样操作,可以在多线程环境下,可以防止当前线程的数据被其他线程修改。另外,由于各个线程之间的数据相互隔离,避免了同步加锁带来的性能损失,大大提升了并发性的性能。

        例如:sqlSession会话对象绑定,避免多个线程使用同一个sqlsession 对象,由于关闭导致异常。


2. 跨函数传递

        数据通常用于同一个线程内,跨类、跨方法传递数据时,如果不用ThreadLocal,那么相互之间的数据传递势必要靠返回值和参数,这样无形之中增加了这些类或者方法之间的耦合度。



        

标签:Java,Thread,get,秘密武器,ThreadLocal,线程,数据,public
From: https://blog.csdn.net/weixin_71491685/article/details/142390482

相关文章