【java并发编程】ThreadLocal线程本地类

 一、简介

ThreadLocal类是作为线程内部的局部变量而提供的。让这些变量在多线程环境下访问(get/set)时能保证各个线程里的变量相对独立于其他线程内的变量。

通过ThreadLocal创建的变量只能被当前线程访问,对其他线程不可见,故别的线程无法访问和修改,也就是说:对线程公有化变成对线程私有化。事实上每个线程中都有一个ThreadLocal变量副本。

相比于锁的性能:

java在使用锁的使用中会导致运行效率降低,ThreadLocal的使用彻底避免对共享资源的竞争,同时又可以不影响效率。ThreadLocal采⽤了“以空间换时间”的⽅式,为每⼀个线程都提供⼀份变量的副本,从⽽实现同时访问⽽互不影响,但因为每个线程都维护着⼀份副本,对内存空间的占⽤会增加。

二、ThreadLocal原理

ThreadLocalMap是定义在ThreadLocal类中的一个静态内部类。而它的结构和HashMap结构极其相似。有一个Entry[]数组存放数据。而这个Entry类是继承自WeakReference类的子类,这一点和HashMap有所不同。

  

get方法

get()方法就是从Thread中取出来ThreadLocalMap,然后通过ThreadLocal对象作为Key取出值;remove()方法则是取出ThreadLocalMap将ThreadLocal对应的数据移除。

set方法

当调⽤set⽅法时,将数据写⼊threadLocals这个Map对象中,这个Map的key为ThreadLocal当前对象,value就是我们存⼊的值。⽽threadLocals本⾝能保存多个ThreadLocal对象,相当于⼀个ThreadLocal集合。

解决hash冲突使用的是“开放寻址法”,区别于hashmap的链表法

缺点

内存泄漏问题

ThreadLocalMap中的Entry的Key是一个弱引用,因此如果在使用后不调用remove方法清除掉会导致对应的value内存泄漏。所以在使用完以后一定要记得调用remove方法清除数据。

三、ThreadLocal使用

public class Main {
    //1、定义一个private static的ThreadLocal对象。
    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) {
        //2、每个线程可以将当前线程需要存放在局部变量中,并且可以从中获取。
        threadLocal.set("");
        String s = threadLocal.get();

        //3、最后在使用完之后,需要将ThreadLocal中的值移除。
        threadLocal.remove();
    }
}

使用场景

主要是用来做线程间数据隔离。

例如:SimpleDateFormat这个工具类,它不是线程安全的,可以通过ThreadLocal在每个线程中放一份,保证线程安全。

四、FastThreadLocal类

FastThreadLocal是netty包的,具体是io.netty.util.concurrent包

1、FastThreadLocal由来

netty为什么要在再造一个FastThreadLocal类呢

因为ThreadLocal效率不高,具体为:

  • 查找当前Thread绑定的变量, 是通过在Map中根据ThreadLocal的hash去查找的,不如数组下标访问快。

  • 使用开放寻址法解决hash冲突时,效率非常低。也就是说当hash的index被占用后,会往后看是否有位置可能存放,如此往复,直到有空位为止.

  • Map扩容涉及到重新计算部分index, 最糟的情况还要挪动元素的位置

  • 当线程执行完, 最好清掉所绑定的threadlocal变量, 不然会内存泄漏. 现实中, 往往会忘记清理.

FastThreadLocal(下文简称ftl)直接使用数组避免了hash冲突的发生。

具体做法是:每一个FastThreadLocal实例创建时,分配一个下标index;分配index使用AtomicInteger实现,每个FastThreadLocal都能获取到一个不重复的下标。当调用ftl.get()方法获取值时,直接从数组获取返回,如return array[index],如下图:

对应ThreadLocal类

在原生的ThreadLocal中有三个角色,Thread,ThreadLocal和ThreadLocalMap。分别对应了Netty的FastThreadLocalThread,FastThreadLocal和InternalThreadLocalMap。

2、如何使用

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
</dependency>

        //1、Netty提供的DefaultThreadFactory 工厂类,创建的线程默认就是 FastThreadLocalThread 类型
        DefaultThreadFactory defaultThreadFactory = new DefaultThreadFactory(FastThreadLocalTest.class);

        //2、创建FastThreadLocal
        FastThreadLocal<String> fastThreadLocal = new FastThreadLocal<>();

        //3、工厂生产线程
        Thread thread = defaultThreadFactory.newThread(() -> {
            //4、设置变量
            fastThreadLocal.set("");
            //5、获取变量
            fastThreadLocal.get();
        });
        //6、启动线程
        thread.start();

猜你喜欢

转载自blog.csdn.net/sumengnan/article/details/125070829