企业站模板,招聘网站开发的流程,衡阳百度推广,主题网站的设计方案ThreadLocal
什么是ThreadLocal
ThreadLoacal类可以为每个线程保存一份独有的变量#xff0c;该变量对于每个线程都是独占的。实现原理为每个Thread类中包含一个ThreadHashMap#xff0c;key为变量的对应的ThreadLocal对象#xff0c;value为变量的值。
在日常使用中该变量对于每个线程都是独占的。实现原理为每个Thread类中包含一个ThreadHashMapkey为变量的对应的ThreadLocal对象value为变量的值。
在日常使用中我们可以通过set(value)方法设置值然后通过get()方法获取值示例代码如下
我们通过线程池执行了五个线程每个线程都有一份自己独有的id变量实现了线程间的数据隔离。
Slf4j
public class ThreadLocalTest {// ThreadLocal 变量每个线程都有自己的副本private static final ThreadLocalInteger id new ThreadLocal();Testpublic void testThreadLocal() {// 创建线程池ExecutorService executorService Executors.newCachedThreadPool();try {for (int i 0; i 5; i ) {int finalI i;executorService.execute(() - {// 设置ThreadLocal 变量 id.set(finalI);// 获取ThreadLocal变量log.info(The id of thread {} is {}, Thread.currentThread().getName(), id.get());});}} finally {id.remove();executorService.shutdown();}}
}传统的HashMap是链地址法发生碰撞放入桶内ThreadLocalMap是开放地址法发生冲突放到后一位 实现原理
那如何实现这种线程间的数据隔离呢一种容易想到的方法是在Map中进行存储每个ThreadLocal变量内维护一个Map其中key为每个线程的IDvalue为对应的值。这样当调用get()方法时会通过hash算法找到对应线程的值。
jdk也确实是这么做的只不过存放元素Map的实现不是HashMap而是ThreadLocalMap解决冲突的做法是开放寻址法即出现冲突就往下一个临近位置找直到找到空位置。
早期的实现中多个线程会共享一个ThreadLocalMap这样随着线程数的增加map就需要扩容会消耗较多的资源
后来每个线程都有自己的一个ThreadLocalMap各自保存自己的局部变量key为变量名value为变量值。
因为局部变量很少所以一般不需要扩容。
内存泄漏
下面这张图是ThreadLocal对应的引用关系可以看到在新的设计下ThreadLocalMap存在于Thread中是和Thread的生命周期一致的。 ThreadLocalMap的key为ThreadLocal对象value是对应的值需要注意的是ThreadHashMap的key会使用弱引用这主要是为了当ThreadLocal为null后除了ThreadHashMap持有的弱引用外没有别的强引用ThreadLocal对象可以被GC回收。
但是这样就导致ThreadHashMap中对应的key为null其value引用的对象不会被GC回收从而出现了内存泄漏。
所以在使用完ThreadLocal变量后尽量使用remove()方法进行清理这样会把key为null的键值对删除。
再回到上面的示例上面的示例我们使用final修饰了ThreadLocal保证其不会被修改这样做会导致ThreadLocal永远存在强引用不会被释放容易发生内存泄露所以需要我们显示调用remove()方法进行清理。
参考链接
ThreadLocal的内存泄露什么原因如何避免