一、简介
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();