关于ThreadLocal的一些认识

ThreadLocal详解 ThreadLocal详解都在里面了.. 阅读详情

在前面讲关于Handler的Looper的时候,我们讲到过Looper的获取和设置,是通过ThreadLocal来操作的,如下:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

在准备Looper的时候,

    public static void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }

在获取Looper的时候,

   /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static Looper myLooper() {
        return sThreadLocal.get();
    }

那么ThreadLocal的作用到底什么呢?为什么会有ThreadLocal的出现呢?

首先,我们先来看下面一个例子吧,如下:

public class HelperTesting {

    private String text = "empty";

    private static HelperTesting helperTesting = new HelperTesting();

    private void printText() {
        System.out.println(text);
    }

    private void changeText(String str) {
        text = str;
    }
    
    private void printWithLine(String str){
        System.out.println("##################### " + str +" #############################");
    }

    public static void main(String[] args) {

        helperTesting.printWithLine(Thread.currentThread().getName() + " start");
        helperTesting.printText();
        
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {                      
                helperTesting.printWithLine(Thread.currentThread().getName() + " start");
                helperTesting.printText();
                helperTesting.changeText("thread1");
                helperTesting.printText();
                helperTesting.printWithLine(Thread.currentThread().getName() + " end");
            }
        });
        
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        helperTesting.printText();  
        helperTesting.printWithLine(Thread.currentThread().getName() + " end");
    }
}


在这个小Demo里面,我们做了几件事情:

1)创建了一个HelperString对象,其有一个 text 字段,初始化其值为 “empty”。

2)创建了一个新的线程,在线程中改变helperString对象的 text 的值为"thread1",运行线程,并打印其值。

3)最后我们在原来的线程中打印出text的值。

运行结果如下:

##################### main start #############################
empty
##################### Thread-0 start #############################
empty
thread1
##################### Thread-0 end #############################
thread1
##################### main end #############################

我们可以看到,在主线程中的 text 值被改变了。

在多线程的编程过程当中,因为thread共享同一段进程空间,所以A线程的运行是完全有可能在不经意间就改变了某个值,而当B线程也去拿这个值的时候,可能就已经不是其想要获取的数值了。

于是有人就想,能不能让线程也拥有其私有的一些空间呢,让存放在这段空间里面的值并不会被其他线程改变,而这就是ThreadLocal的出现。

注意,其出现只是为了保证线程数据的私有性,并不是为了解决线程间的同步问题,我估计这也是其为什么叫Thread Local 的原因。

接下来,我们再通过一个Demo来看看,怎么使用ThreadLocal,如下:

public class HelperTestingSec {

    private ThreadLocal<String> sThreadLocal = new ThreadLocal<String>();

    private static HelperTestingSec helperTesting = new HelperTestingSec();

    private HelperTestingSec(){
        sThreadLocal.set("empty");
    }
    
    private void printText() {
        System.out.println(sThreadLocal.get());
    }

    private void changeText(String str) {
       sThreadLocal.set(str);
    }
    
    private void printWithLine(String str){
        System.out.println("##################### " + str +" #############################");
    }        
    public static void main(String[] args) {

        helperTesting.printWithLine(Thread.currentThread().getName() + " start");
        helperTesting.printText();      
        
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                helperTesting.printWithLine(Thread.currentThread().getName() + " start");
                helperTesting.printText();
                helperTesting.changeText("thread1");
                helperTesting.printText();
                helperTesting.printWithLine(Thread.currentThread().getName() + " end");
            }
        });
                        
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        helperTesting.printText();
        helperTesting.printWithLine(Thread.currentThread().getName() + " end");
    }
}

这个Demo中做的事情,其实跟上一个Demo类似,唯一不同的是,在这个Demo中,我们利用一个ThreadLocal对象来存放这个String的值,运行结果如下:

##################### main start #############################
empty
##################### Thread-0 start #############################
null
thread1
##################### Thread-0 end #############################
empty
##################### main end #############################

从上面的结果,我们可以看到,同一个helperTestingSec对象,在 main线程中,其 sThreadLocal中存放的对象一直是empty。

而在新建的线程当中呢,当在调用changeText进行设值之前,其value是null的,也就是说在新线程中,虽然是同一个对象,但是其值是不一样的,即使我们在新线程中改变了其值,也不会影响其在Main线程中的值。

可见,通过 ThreadLocal,我们就做到了线程间的数据独立了。

那么,ThreadLocal的实现原理是什么,为什么其能做到这个事情呢,我们还是想了解一下其内部的构造的,看一下能不能学到点什么东西,对吧。

按照顺序,一般情况下,我们总是先放东西进去,再去拿东西出来的,所以我们先看一下ThreadLocal 的 set 方法。

有一点要注意的是,这里讲解的ThreadLocal的实现是Android里面的实现,JDK中的实现方式有点不一样,但是原理是一样的。

    public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }

在Demo2中,我们调用sThreadLocal的 set方法的时候,我们就进入了上面的逻辑。

可以看到,第一步,就是先利用Thread.currentThread拿到当前的线程,再根据这个线程去获取一个叫做Values的对象。再看一下values函数,我们可看到其是从哪里去获取这个Values对象的,如下:

   /**
     * Creates Values instance for this thread and variable type.
     */
    Values initializeValues(Thread current) {
        return current.localValues = new Values();
    }

    /**
     * Gets Values instance for this thread and variable type.
     */
    Values values(Thread current) {
        return current.localValues;
    }

可以看到,其是从current这个线程里去获取Values对象的,而如果不存在这个对象,其就会调用initializeValues方法,为线程的localValues创建一个新的Values对象。

最近再调用values的put方法,以当前ThreadLocal对象为key值,将我们的valule给存放到这个Values对象中,我们会马上意识到,Values实现的应该是一个类似Map的键值对的数据结构。

从这里,我们就可以意识到:

1)在每个线程中都存在一个Values对象。

2)无论我们在哪个线程中调用ThreadLocal对象,都会先去当前线程中取得一个Values对象(如果不存在,就创建一个)再对这个Values对象进行键值操作,比如ThreadLocal的Get方法,如下:

    public T get() {
        // Optimized for the fast path.
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values != null) {
            Object[] table = values.table;
            int index = hash & values.mask;
            if (this.reference == table[index]) {
                return (T) table[index + 1];
            }
        } else {
            values = initializeValues(currentThread);
        }

        return (T) values.getAfterMiss(this);
    }

3)一个ThreadLocal对象其实是可以给所有线程共享的,因为其只是作为一个入口和一个WeakRefreence的Key值,真正的数据都是存在Values中,而这个Values对象是线程独有,且在当前线程中是唯一的。

所以这也是为什么在Looper类中,sThreadLocal对象是一个静态的对象就行了。

而Values的内部实现,其实是用一个Object数组来做为数据结构的,偶数位(0,2,4....)等作为Key值,其存放是指向ThreadLocal对象本身的一个WeakReference, 奇数位(1,3,5....)等作为的Value值,存放的是我们设置的值,从上面 get方法中, 我们也可以略窥一二。

在这里,推荐两边文章,相信对大家了解ThreadLocal有一定的帮助。

When and how to use a ThreadLocal
How is ThreadLocal implemented?



【ThreadLocal源码】深入浅出ThreadLocal ThreadLocal提供线程局部变量。这些变量与正常的变量不同,因为每一个线程在访问ThreadLocal实例的时候,都有自己的、独立初始化的变量副本。ThreadLocal实例通常是类中的私有静态变量,使用它的目的是希望将状态(用户ID、事务ID等)与线程关联起来。1)、前面执行的方法保存了信息后,后续方法可以通过ThreadLocal 直接获取到,避免了传参,类似于全局变量的概念,例如当前登录用户放入ThreadLocal 中,避免每个方法一直传递过去,直接从ThreadLocal获取即可; 阅读详情

相关推荐

山东大学FPGA课程实验一 加法器设计

电子设计自动化与FPGA设计,实验一 加法器设计

2503_91283667的博客 869

Threadlocal原理、用法简介及日常问题分析

Threadlocal原理、用法简介及日常问题分析

qq_31310291的博客 712

手把手教你配置SAP EWM HU类型及号段分配(含常见错误排查)

本文详细介绍了SAP EWM系统中HU类型及号段分配的配置方法,包括环境准备、包材主数据配置、存储类型关联及号段管理。通过实战案例和常见错误排查,帮助用户高效完成HU配置,提升仓储管理效率。重点讲解了EWM后台配置和号段配置的关键步骤与技巧。

weixin_33670786的博客 443

ThreadLocal使用

首先直接看如何使用ThreadLocal public class TaobaoPurchaseTaskNettyHandler { private static final String END_WORKING_TIME = "230000"; private static Logger logger = LoggerFactory.getLogger(TaobaoPurchase

yunyuan_zhang的博客 254

STM32实现薄膜压力传感器数据采集(标准库和HAL库实现)

薄膜压力传感器采集压力数据

XiaoCaiDaYong的博客 3万+

ESP32-P4开发板实战:从零配置VSCode环境到点亮MIPI-DSI屏幕(避坑指南)

本文详细介绍了ESP32-P4开发板在VSCode环境下的配置流程,包括SDK安装、idf_component.yml依赖管理,以及MIPI-DSI屏幕驱动的实现方法。通过实战指南帮助开发者避开常见配置陷阱,快速完成从环境搭建到屏幕点亮的全流程,适用于物联网和嵌入式开发场景。

pp12345的博客 742

ThreadLocal 使用和详解避坑

线程栈用于存储方法调用的信息,而线程本地存储则是每个线程私有的存储空间,用于存储线程的局部变量。ThreadLocal类提供了一种简单的方式来实现线程本地存储,它允许将线程特定的值与每个线程关联起来。线程池中的线程重用:在使用线程池的情况下,线程会被重用。如果线程在使用ThreadLocal之后没有进行清理操作,可能会导致后续使用该线程时,获取到之前线程的ThreadLocal变量值。通过将对象存储在ThreadLocal变量中,可以保证每个线程都有自己的私有对象,从而避免竞争条件的发生。

DS_Watson的博客 623

告别 RPA 和 Selenium,MCP + Playwright 成为自动化开发新宠!

摘要: 随着数字化转型加速,传统自动化工具(如RPA和Selenium)在复杂场景中暴露出开发成本高、灵活性不足等问题。新一代解决方案MCP(Model Context Protocol)与Playwright的结合,通过AI与自动化的无缝对接,实现了智能化的任务处理。MCP支持自然语言交互和上下文感知,开发者无需编写复杂代码即可生成自动化脚本;Playwright则提供多浏览器支持、高性能和易用性。二者结合显著提升开发效率,使自动化更智能、灵活。文章还对比了VS Code与Trae中配置Playwrigh

我先测了 2208

threadlocal使用场景_ThreadLocal,似如爱的logo

ThreadLocal概述ThreadLocal 字面意思:“线程变量”,即它为每个使用该变量的线程都提供了一个独有的副本,互不影响且决了线程并发访问的冲突。线程并发:在多线程并发场景下使用。传递数据:可以通过ThreadLocal在同一线程,不同组件中传递公共变量。线程隔离:每个线程变量都是独立的,不会相互影响。我接触ThreadLocal最多的就是上下文数据的访问:publiccl...

weixin_39610785的博客 151

大数据之数据抽取架构演变过程

而当时我们搭建的Flink集群是基于YARN进行资源管理和调度的,因为YARN中不仅仅包含着FLInk的资源,也有hive,zoozie,hdfs等等的资源,这样容易造成yarn资源的过度使用,容易引起整个底座的cpu以及内存的使用问题,经过排查,主要是由于FLink的使用资源引起的,所以后续就把Flink从整个集群中剥离出来,进行资源的隔离,最后Flink采用了StandAlone HA部署(但是这个目前还是存在问题,后续根据项目进行优化)'topic' = '表名',二、数据抽取架构的演变。

天道酬勤 867

【学习系列】SAP RAP 2:创建第一个RAP应用

本文介绍了从零开始创建基于SAP RAP(ABAP RESTful Application Programming Model)的Fiori应用的完整流程。首先说明了学习RAP需要结合官方文档和教程,并提供了相关资源链接。文章详细讲解了开发环境的准备要求,包括系统版本、开发工具和基本知识。重点演示了如何通过8个步骤创建支持CRUD操作和草稿功能的RAP应用:1)创建数据库表;2)定义CDS视图实体;3)创建投影视图;4)定义行为;5)创建行为投影;6)定义服务;7)绑定服务;8)添加UI注解。

DeveloperMrMeng的博客 1498

Syong : ThreadLocal

ThreadLocal What 英文API文档: This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its o...

CSY_LOVE_GYY的博客 264

vue3:最新实现腾讯人脸核身+增强版人脸核身使用方法及示例源码,Vue3如何使用腾讯云慧眼人脸核身,提供人脸核身案例、身份信息核验、活体检测与核身比对等示例代码(后端spring与thinkphp)

vue3接入腾讯人脸核身,腾讯云人脸核身Vue3+后端代码,H5人脸核身,vue3 H5如何使用腾讯人脸核身sdk,vue3+thinkphp腾讯人脸核身,Spring Boot接入腾讯云人脸核身教程,hinkphp腾讯人脸核身,vue3人脸核身插件接入流程,vue+spring boot实现腾讯人脸核身,vue3接入腾讯云【增强版人脸核身接入】,vue3 h5微信公众号网页腾讯云慧眼人脸核身,tvue3接入人脸核身SDK,vue3+typescrip+vite接入慧眼实名认证,vue3腾讯云活体人脸核身(

✅ 精选推荐文章 4119

ThreadLocal原理、ThreadLocal的使用、ThreadLocal内存泄漏、ThreadLocal的坑

文章目录背景说明一:ThreadLocal的原理1.1 什么是ThreadLocal变量1.2 ThreadLocal实现原理1.3 ThreadLocalMap实现原理1.4 ThreadLocalMap结构图二:ThreadLocal的使用2.1 创建ThreadLocal工具类2.2 在拦截器/过滤器中通过用户token获取登录信息,并与当前线程绑定2.3 业务处理时获取用户信息2.4 当前请求结束时,销毁ThreadLocal变量三:ThreadLocal的坑、ThreadLocal内存泄漏3.1

weixin_49114503的博客 8910

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

ThreadLocal

ThreadLocal,即线程变量,是一个以ThreadLocal对象为键、任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。 用法:可以通过set(T)方法来设置一个值,在当前线程下再通过get()方法获取到原先设置的值。 好处:两个方法的调用不在一个方法或者类中只需要在同一线程中就可以获取到对应的值。 代...

w8827130的博客 433

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

muduo之ThreadLocal

ThreadLocal用来存储线程私有数据的类。 // Use of this source code is governed by a BSD-style license // that can be found in the License file. // // Author: Shuo Chen (chenshuo at chenshuo dot com) #ifn...

1万+

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

理解ThreadLocal

ThreadLocal是什么早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”。其实,ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为T

Java之道 27万+

ThreadLocal对象

ThreadLocal,即线程变量,是一个以ThreadLocal对象为键、任意对象为值的存储结构。这 个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值,也就是是说可以让线程与保存值的关联起来,让每个线程都有一个自己的变量,实现线程安全。 可以通过set(T)方法来设置一个值,在当前线程下再通过get()方法获取到原先设置的值。 在代码清单4-15所示的例子中,构建了一个常用的Profiler类,它具有begin()和end()两个 方法,而e

tian830937的专栏 720

threadLocal原理

一.ThreadLocal的作用   ThreadLocal提供的实际上就是一种线程隔离,不同的线程的变量归属不同的线程,线程之间不能想问访问各自的变量,当线程生命周期结束的时候,线程会被销毁。 二.ThreadLocal的原理 Thread类中有一个成员变量threadLocals: 这个ThreadLocalMap 是ThreadLocal类中一个静态内部类; ThreadLocal类中最主要的就是set 方法get方法; set方法 这个getMap(t); 传入一个Thr.

sam19911的博客 5531

ThreadLocal 详解

1. ThreadLocal 是用来干什么的 ThreadLocal 是一个以 ThreadLocal 对象为键、任意对象为值的存储结构,这个结构被附带在线程上,也就是说一个线程可以根据一个 ThreadLocal 对象查询到一个绑定在这个线程上的值。 2. ThreadLocal 示例 代码如下: public class TestThreadLocal { private static final ThreadLocal<String> STRING_THREAD_LOCAL = n

明月燃雨 7813

线程副本之ThreadLocal

ThreadLocal,即线程变量,是一个以ThreadLocal对象为键,任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到一个绑定到这个线程上的值。 可以通过set(T)方法来设置一个值,在当前线程下再通过get()方法 获取到原先设置的值。 **代码示例:**构建了一个常用的Profiler类,它具 有begin()和end()两个方法,而end()方法返回从begin()方法调用开始到 end()方法被调用时的时间差,单位是毫秒。 publi

weixin_43902350的博客 316

详解ThreadLocal

Thread存储线程独有的变量(存储多个变量(值)时,可以创建多个ThreadLocal用来存储),以供在整个线程存活过程中取用。这种能力由ThreadLocal中的ThreadLocalMap实现。Thread在存储自己独有变量时,ThreadLocal实例在该Thread环境内使用set方法实现变量(值)存储。(继续探究原理)实际是静态内部类ThreadLocalMap将该变量(值)以key=ThreadLocal实例,value=变量(值)的键值对对象Entry形式存储到了ThreadLocalMap

文化园 4543

利用threadLocal实现对执行时间的统计

import java.util.Properties; import java.util.concurrent.TimeUnit; import static java.util.concurrent.TimeUnit.*; /* * 利用 threadLocal 实现 对执行时间的统计 * */ public class Profiler { private static final ThreadLocal<Long> TIME_THREADLOCAL = new Thread.

yt438936731的博客 1223
上一篇: Android中利用Handler实现消息的分发机制(三)
下一篇: 跟面试官讲Binder(一)
linmiansheng
博客等级 码龄20年 507粉丝 111原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值