Loading [MathJax]/jax/input/TeX/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >ThreadLocal解析与最佳实践

ThreadLocal解析与最佳实践

作者头像
LNAmp
发布于 2018-09-05 07:46:34
发布于 2018-09-05 07:46:34
1.2K00
代码可运行
举报
运行总次数:0
代码可运行

概述

用于同一个线程内的方法要共享某些变量或状态的时候,提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度

源码解读

源码的阅读主要集中在几个关键方法

构造函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  /**
     * Creates a thread local variable.
     */
    public ThreadLocal() {
    }

可以看出,默认的构造函数什么都没有干,但如果需要设置初始值怎么办

initialValue()

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    protected T initialValue() {
        return null;
    }

使用者可以通过继承ThreadLocal覆盖该方法来设置初始值,该值在第一次调用get()方法时被调用,该方法在整个ThreadLocal的生命周期中应该只对调用一次,除非用户显示地调用了remove(),然后又调用get()时会再次调用initialValue

get()

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }

get方法中可以看出先得到当前线程的threadlocalmap,如果不存在该map(首次调用get()),则调用setInitialValue(),如果存在则得到当前Key对应的值

setInitialValue()

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

先调用intialValue得到初始值,然后得到该线程对应的ThreadLocalMap,然后在Map中set初始值,如果没有ThreadLocalMap则创建,并设置当前TheadLocal初始值.从上可以看出,初始化的时候可能做两件事 1、已有map 则将ThreadLocal作为key,initialValue为value放入到map中

2、没有map 新建一个ThreadLocalMap,并将<key,value>放入其中

createMap()

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

set(T value)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
   public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

和setInitialValue方法类似

remove()

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

比较重要的一个方法,将当前的threadlocal变量从map中移除。

tips

比较重要的一点是,ThreadLocal,Thread,ThreadLocalMap的设计 目前的设计是Thread中有ThreadLocalMap,Map中以ThreadLocal为key,这种设计非常的清晰,由于在ThreadLocalMap中ThreadLocal是以WeakReference的形式存在的,所以其引用链或如下所示,也会产生GC疑问:ThreadLocal被回收,但是map中的entry一直不能回收的问题。 所以引出了最佳实践问题

threadlocal的引用链

ThreadLocal引用链.png

最佳实践

最佳实践的方法参见google guava eventbus中对于ThreadLocal的使用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    private final ThreadLocal<Boolean> dispatching;
    
    this.dispatching = new ThreadLocal() {
                protected Boolean initialValue() {
                    return Boolean.valueOf(false);
                }
            }
    if(!((Boolean)this.dispatching.get()).booleanValue()) {
                this.dispatching.set(Boolean.valueOf(true));

                Dispatcher.PerThreadQueuedDispatcher.Event nextEvent;
                try {
                    while((nextEvent = (Dispatcher.PerThreadQueuedDispatcher.Event)queueForThread.poll()) != null) {
                        while(nextEvent.subscribers.hasNext()) {
                            ((Subscriber)nextEvent.subscribers.next()).dispatchEvent(nextEvent.event);
                        }
                    }
                } finally {
                    this.dispatching.remove();
                    this.queue.remove();
                }
            }
  • 采用匿名内部类赋初始值
  • 显式调用get()、set()
  • 在不用的时候显式地remove()掉
  • 对于显示的remove特别重要,因为这样可以避免entry不被GC的情况 如果为了避免ThreadLocal被GC,可以加强ThreadLocal的引用,将其声明成private static

致谢

本文参考了解密ThreadLocal,引用了文中的图片,感谢作者,侵删。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016.08.04 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
一文说清楚ThreadLocal
当多个线程对同一变量进行写操作的时候,容易出现线程安全问题,所以就会用到对应的锁和其他一些方法,我们先不介绍锁,先介绍ThreadLocal, ThreadLocal字面意思本地线程,ThreadLocal使每个线程之间是隔离的,数据是独立的,我们使用过session都知道 session是一个会话,我们可以用它来存储一些用户的基本信息,这样每个用户在服务端都能取到,ThreadLocal也可以做到,ThreadLocal将相应的信息存储在当前的线程中,只有当前线程能够访问,其他线程不能访问,这样就能保证线程安全,其实ThreadLocal是一个定制化的Map。
小四的技术之旅
2022/07/26
3280
面试|再次讲解Threadlocal使用及其内存溢出
浪尖整理本文主要是想帮助大家完全消化面试中常见的ThreadLocal问题。希望读懂此文以后大家可以掌握(没耐心的可以直接阅读底部总结):
Spark学习技巧
2019/07/09
9330
面试|再次讲解Threadlocal使用及其内存溢出
ThreadLocal 源码解读
首先我们看到的是 Thread 中有一个属性 threadLocals,它的类型是 ThreadLocalMap,封装类型是 default(表示它只能在包内可见),jdk 是这么介绍它的:与此线程有关的 ThreadLocal 值,该映射由 ThreadLocal 类维护。 啥意思呢?那就来看看 ThreadLocalMap 是啥玩意!
JMCui
2019/12/11
3230
面试Threadlocal源码解析
今天我们讲一下高频面试题,Threadlocal,他是JDK1.2就已经有了,他是为每一个使用该变量的线程提供独立的副本,可以做到线程间的数据隔离,每一个线程都可以访问各自内部的副本变量。
小土豆Yuki
2020/06/15
2660
深入理解Threadlocal的实现原理
文章开头我想说,这是一篇面向不怎么懂  Threadlocal 的朋友的博客,所以有的人会觉得有点啰嗦,但不论您水平高低,相信耐着性子看完也一定会有收获。
矿泉水
2018/05/11
8823
<一>深入理解Threadlocal的实现原理
文章开头我想说,这是一篇面向不怎么懂  Threadlocal 的朋友的博客,所以有的人会觉得有点啰嗦,但不论您水平高低,相信耐着性子看完也一定会有收获。
用户2141593
2019/02/20
1.7K0
ThreadLocal (中) 原理具体实现详解
由该图可知,Thread类中有一个threadLocals和一个inheritableThreadLocals,它们都是ThreadLocalMap类型的变量,而ThreadLocalMap是一个定制化的HashMap。在默认情况下,每个线程中的这两个变量都为null,只有当线程第一次调用ThreadLocal的set()或get()方法时才华创建它们。其实每个线程的本地变量不是存放在ThreadLocal实例里面,而是存放在具体线程内存空间中。ThreadLocal就是一个工具壳,它通过set方法把value值放入调用线程的threadLocals里面并存放起来,当调用线程调用它的get方法时,再从当前线程的threadLocals变量里面将其拿出来使用。如果调用线程一直不重质,那么这个本地变量会一直存放在调用线程的threadLocals变量里面,所以当不需要使用本地变量的时候可以通过调用ThreadLocal变量的remove()方法,从当前线程的threadLocals里面删除该本地变量。另外,Thread里面的threadLocals为何被设计为map结构?很明显是因为每个线程可以惯量多个ThreadLocal变量。
YanL
2020/04/26
7040
ThreadLocal (中) 原理具体实现详解
为什么 ThreadLocal 可以做到线程隔离?
对于 ThreadLocal 我们都不陌生,它的作用如同它的名字——用于存放「线程本地」变量。
刘水镜
2022/07/29
3000
ThreadLocal及InheritableThreadLocal的原理剖析
我们知道,线程的不安全问题,主要是由于多线程并发读取一个变量而引起的,那么有没有一种办法可以让一个变量是线程独有的呢,这样不就可以解决线程安全问题了么。其实JDK已经为我们提供了ThreadLocal这个东西。
Java学习录
2019/04/18
5910
ThreadLocal 源码解析
ThreadLocal 顾名思义就是在每个线程内部都会存储只有当前线程才能访问的变量的一个副本,然后当前线程修改了该副本的值后而不会影响其他线程的值,各个变量之间相互不影响。
Java技术编程
2020/05/25
3950
ThreadLocal 类
ThreadLocal 并不是一个Thread,而是 ThreadLocalVariable(线程局部变量)。也许把它命名为 ThreadLocalVar更加合适。线程局部变量就是为每一个使用该变量的线程都提供一个变量值的副本,是 Java中一种较为特殊的线程绑定机制,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。ThreadLocal是除了加锁这种同步方式之外的另一种保证多线程访问出现线程不安全的方式。
Java架构师必看
2021/05/14
5150
ThreadLocal 类
ThreadLocal分析
ThreadLocal是一个本地线程副本变量工具类。主要用于将私有线程和该线程存放的副本对象做一个映射,各个线程之间的变量互不干扰,在高并发场景下,可以实现无状态的调用,特别适用于各个线程依赖不通的变量值完成操作的场景。
爱撸猫的杰
2019/08/07
7810
ThreadLocal分析
抛出这8个问题,检验一下你到底会不会ThreadLocal,来摸个底~
ThreadLocal类是用来提供线程内部的局部变量。让这些变量在多线程环境下访问(get/set)时能保证各个线程里的变量相对独立于其他线程内的变量。
Java编程指南
2020/07/24
7420
抛出这8个问题,检验一下你到底会不会ThreadLocal,来摸个底~
java并发之无同步方案-ThreadLocal
1.ThreadLocal  介绍2.ThreadLocal  应用3.ThreadLocal  源码解析3.1解决 Hash 冲突4.ThreadLocal 特性5.4.ThreadLocal 内存泄露问题
Java宝典
2020/11/30
4080
ThreadLocal用法及原理
Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
烂猪皮
2019/04/26
1.6K0
聊一聊线程变量绑定之ThreadLocal
这里我们从源码角度来聊一聊 ThreadLocal 的原理。先来看一看它的属性和方法:
山行AI
2019/12/19
9620
聊一聊线程变量绑定之ThreadLocal
ThreadLocal的使用及原理分析
ThreadLocal称作线程本地存储。简单来说,就是ThreadLocal为共享变量在每个线程中都创建一个副本,每个线程可以访问自己内部的副本变量。这样做的好处是可以保证共享变量在多线程环境下访问的线程安全性。
日薪月亿
2019/05/14
5750
ThreadLocal的使用及原理分析
精通高并发与多线程,却不会用ThreadLocal?
之前我们有在并发系列中提到 ThreadLocal 类和基本使用方法,那我们就来看下 ThreadLocal 究竟是如何使用的!
蔡不菜丶
2020/11/11
5160
ThreadLocal原理探究
多线程访问同一个共享变量特别容易出现并发问题,特别是多个线程需要对一个共享变量进行写入时候,为了保证线程安全,一般需要使用者在访问共享变量的时候进行适当的同步,如下图:
加多
2018/09/06
4190
ThreadLocal原理探究
探索JAVA并发 - ThreadLocal
SimpleDateFormat是我们常用的日期格式化工具,但熟悉的朋友都知道它是线程不安全的。
acupt
2019/08/26
4200
相关推荐
一文说清楚ThreadLocal
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验