JUC学习系列五(ThreadLocal)

2023-10-30

该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。

每个线程都保持对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。

一、ThreadLocal简单使用

三个线程,线程1依次打印1、2、3、4、5......线程2依次打印1、3、5、7、9.....线程3依次打印1、2、4、8、16......

public class threadLocal {

    public static void main(String[] args) {
        //
        final ThreadLocal<Integer> tl1=new ThreadLocal<Integer>(){
            @Override
            protected Integer initialValue() {
                //给ThreadLocal附上初始值
                return 1;
            }
        };

        new Thread(new Runnable() {
            public void run() {
                while (true){
                Integer a=tl1.get();
                System.out.println(Thread.currentThread().getName()+" "+a);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    tl1.set(++a);
                }
            }
        }).start();
        new Thread(new Runnable() {
            public void run() {
                while (true) {
                    Integer b = tl1.get();
                    System.out.println(Thread.currentThread().getName() + " " + b);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    tl1.set(b + 2);
//                    tl1.set(++b);
                }
            }
        }).start();
//        new Thread(new Runnable() {
//            public void run() {
//                while (true) {
//                    Integer c = tl1.get();
//                    System.out.println(Thread.currentThread().getName() + " " + c);
//                    try {
//                        Thread.sleep(1000);
//                    } catch (InterruptedException e) {
//                        e.printStackTrace();
//                    }
//                    tl1.set(c);
//                }
//            }
//        }).start();


     }

}

为了看的直观,这里只有前两个线程执行,第三个线程数值增率太大,给注掉了

 ThreadLocal类的部分源码分析:

    //获取线程中值
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
    //初始化方法
    private T setInitialValue() {
        //其实等价于 value =  null
        T value = initialValue();
        //获取当前线程
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            //创建ThreadLocalMap
            createMap(t, value);
        return value;
    }
    //设置值
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            //创建ThreadLocalMap
            createMap(t, value);
    }
    
     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

   /**
    * @Author Loujitao
    * @Date 2018/7/24
    * @Time  9:51
    * @Description:
    * ThreadLocalMap 是ThreadLocal的内部类,在Thread类中有引用
    * 即每个Thread类,都有自己的成员变量ThreadLocalMap,不会共享;
    * 而ThreadLocal是获取线程自己的ThreadLocalMap,然后往里面放值
    * 所以实现了线程间的数据隔离
    */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

 在每个Thread类里面,都有一个ThreadLocalMap的引用,不过它一直是null的。这样在不使用它的时候也不需要占用Thread的内存,当使用到ThreadLocal的时候,便将它实例化出来,用来存储线程自己的数值,保证线程间数据隔离。

取值的过程:使用时的get()方法

 调用ThreadLocal里的get()方法

 ThreadLocalMap里的getEntry()。

 ThreadLocalMap是怎么存储值得。

 

 

好了,以上大致就是ThreadLocal的使用方式,以及为什么它是线程间数据隔离的原因了。觉得有帮助的小伙伴可以点下关注,发现有问题的博客也可以在下方评论出留言,我会验证并及时更正的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JUC学习系列五(ThreadLocal) 的相关文章

  • 并发编程集合

    转载自郑金维老师 一 synchronized 一 原子性 有序性 可见性 1 1 原子性 数据库的事务 ACID A 原子性 事务是一个最小的执行的单位 一次事务的多次操作要么都成功 要么都失败 并发编程的原子性 一个或多个指令在CPU执
  • Java 线程池ExecutorService 等待队列问题

    本人博客原地址 Java 线程池ExecutorService 等待队列问题 创作时间 2019 09 30 11 12 35 1 首先看下Executor获取线程池 这样方式 可以设置线程池的大小 但是了解线程池的内部原理的情况下 这样的
  • Java 多线程事务回滚 ——多线程插入数据库时事务控制

    背景 日常项目中 经常会出现一个场景 同时批量插入数据库数据 由于逻辑复杂或者其它原因 我们无法使用sql进行批量插入 串行效率低 耗时长 为了提高效率 这个时候我们首先想到多线程并发插入 但是如何控制事务呢 直接上干货 实现效果 开启多条
  • 并发编程JMM系列之重排序和顺序一致性

    前言 昨天我们接触到了什么是Java内存模型以及两种Java并发模型 并对JMM有了一些初步的认识和了解 我们在上节有提到JMM的重排序规则 但是讲的不详细 今天我们再重点聊下重排序这个东西 以及顺序一致性内存模型 OK 开始我们今天的并发
  • 多线程-Thread类的常用方法及使用场景

    众所周知 操作线程就必须熟读线程的API方法 万一你开个多线程刹不住车就歇菜了 下面就介绍一些API基本用法 包括sleep join yield interrupt sleep 让当前线程睡一会 原生用法Thread sleep 毫秒 会
  • JUC常用到的类

    JUC java util concurrent 并发包中包含了许多并发编程中需要用到的类 锁 如ReentratLock ReadWriteLock ReentrantLock重入锁 可以替代synchronized使用 并且有更多强大的
  • 线程通信基础示例(synchronized 与 Lock + Condition实现线程通信)

    目录 一 synchronized 实现线程通讯 代码示例 二 Lock Condition 实现线程通讯 代码示例 Lock Condition 实现线程通讯的优点 一 synchronized 实现线程通讯 什么是线程通讯 可以将线程分
  • 生产者与消费者问题?

    生产者消费者模式是并发 多线程编程中经典的设计模式 简单来看 就是一个类负责生产 一个类负责消费 举例来说 一个变量 生产者不断增加这个变量 消费者不断减少这个变量 在互联网应用中 抢票机制就是应用了该模式 比如大麦网演唱会门票抢票 123
  • JUC常用到的类

    JUC java util concurrent 并发包中包含了许多并发编程中需要用到的类 锁 如ReentratLock ReadWriteLock ReentrantLock重入锁 可以替代synchronized使用 并且有更多强大的
  • ReentrantLock实现PV操作-模拟多线程竞争数据库连接池资源场景

    使用ReentrantLock Condition模拟PV操作 实现多线程竞争数据库连接池资源 资源耗尽后阻塞等待 归还资源后唤醒阻塞线程的场景 代码中为10个线程竞争5个数据库连接资源 ConnectionPool class 连接池 C
  • shell编程笔记3--shell并发

    shell编程笔记3 shell并发 shell编程笔记3 shell并发 介绍 并发方法 1 简单后台方式 2 普通控制并发量方式 3 通过管道控制并发量 参考文献 shell编程笔记3 shell并发 介绍 在shell中适当使用并发功
  • Java 线程池的submit的使用与分析.md

    在Java5以后 通过Executor来启动线程比用Thread的start 更好 在新特征中 可以很容易控制线程的启动 执行和关闭过程 还能使用线程池的特性 上一篇我们介绍了线程池的基本用法和特性 我们用的最多的是ExecutorServ
  • 从0实现基于Linux socket聊天室-实现聊天室的公聊、私聊功能-4

    前面文章链接如下 从0实现基于Linux socket聊天室 多线程服务器模型 1 从0实现基于Linux socket聊天室 多线程服务器一个很隐晦的错误 2 从0实现基于Linux socket聊天室 实现聊天室的登录 注册功能 3 上
  • CUDA编程问题记录:能否用CPU多线程调用CUDA核函数

    问题 能否在主机端创建CPU多线程 在每个线程里调用设备端核函数的caller函数 进而实现进一步的并行运行 例如有5张图片 对于每张图片都有N个GPU线程对其进行像素操作 但是此时是逐一对这5张图片处理的 想在主机端创建5个CPU线程 每
  • Semaphore 源码分析

    需要提前了解的知识点 AbstractQueuedSynchronizer 实现原理 类介绍 Semaphore 信号量 是用来控制同时访问特定资源的线程数量 它通过协调各个线程 以保证合理的使用公共资源 比如控制用户的访问量 同一时刻只允
  • ThreadPoolExecutor源码解析

    ThreadPoolExecutor源码解析 一 新建线程池的是构造方法 public ThreadPoolExecutor int corePoolSize int maximumPoolSize long keepAliveTime T
  • 深入理解synchronized底层原理,一篇文章就够了!

    文章目录 前言 一 synchronized的特性 1 1 原子性 1 2 可见性 1 3 有序性 1 4 可重入性 二 synchronized的用法 三 synchronized锁的实现 3 1 同步方法 3 2 同步代码块 四 syn
  • 进程、线程、管程、纤程、协程概念以及区别

    进程 进程是指在操作系统中能独立运行并作为资源分配的基本单位 由一组机器指令 数据和堆栈等组成的能独立运行的活动实体 进程在运行是需要一定的资源 如CPU 存储空间和I O设备等 进程是资源分配的基本单位 进程的调度涉及到的内容比较多 存储
  • brpc源码解析(十七)—— bthread上的类futex同步组件butex详解

    文章目录 一 futex简介 二 butex源码解析 2 1 butex相关数据结构 2 2 butex主要机制 2 2 1 butex wait 2 2 2 butex wake 我们知道在linux 下 锁和其他一些同步机制都会用到fu
  • 并发编程 (6)一不小心就死锁了,怎么办?

    在上一篇文章中 我们用 Account class 作为互斥锁 来解决银行业务里面的转账问题 虽然这个方案不存在并发问题 但是所有账户的转账操作都是串行的 例如账户 A 转账户 B 账户 C 转账户 D 这两个转账操作现实世界里是可以并行的

随机推荐

  • vue 中 ref 、$refs 的用法

    ref 的三种用法 1 ref 加在普通的元素上 用this ref name 获取到的是dom元素 2 ref 加在子组件上 用this ref name 获取到的是组件实例 可以使用组件的所有方法 3 如何利用 v for 和 ref
  • 凸优化及拉格朗日对偶问题

    只记录机器学习方法中需要用到的最优化知识 不做系统总结 持续更新ing 文章目录 1 凸优化 1 凸集 2 凸性条件 3 凸规划 4 凸规划性质 5 凸优化问题 2 拉格朗日函数及其对偶问题 1 拉格朗日函数 含KKT条件 2 拉格朗日对偶
  • pnpm:简介

    一 概念 performant npm 意味 高性能的 npm pnpm由npm yarn衍生而来 解决了npm yarn内部潜在的bug 极大的优化了性能 扩展了使用场景 被誉为 最先进的包管理工具 二 特点 速度快 节约磁盘空间 支持m
  • keras测试环境搭建

    使用的是windows 22h2 在windows中测试 目前看 如果所有用到的库都是pip可以安装的 就可以在windows测试 考虑cuda cudnn对于操作系统的影响太大了 所以只测试了DirectML 安装python3 8 安装
  • 密码爆破总结

    密码爆破经验分享 什么是密码爆破 密码爆破 通过不断输入密码试错 直到成功为止 密码爆破原理 原理 通过不断请求连接 修改连接中的参数 把对应密码的参数遍历字典 自己创建 或者网上获取 根据连接返回的参数来确定是否爆破成功 总结 建立连接
  • MacOS提示【您没有权限来打开应用程序“XXX”。】的完美解决办法

    前言 在 macOS Big Sur 系统下运行一些注册机类的应用程序 大概率会提示 您没有权限来打开应用程序 XXX 请联系您的电脑或网络管理员以获得帮助 信息 具体如下图 下面给出解决办法 说明 本人电脑为新款 MacBook Pro
  • vuex内容及使用详解

    Vuex是什么 Vuex 是一个专为 Vue js 应用程序开发的状态管理模式 它采用集中式存储管理应用的所有组件的状态 并以相应的规则保证状态以一种可预测的方式发生变化 说起状态管理模式 就不得不提起一个名词 单向数据流 单向数据流 在V
  • ubuntu vi/vim退出文件

    要退出 但是不保存 按esc先 然后输入 q 回车键即可退出 无法退出 则在 q 加入一个叹号回车键就行了 要退出保存的 则输入 wq 强制保存退出 则输入 wq 看到此时已经退出 在等待命令状态 以nano开头编辑文件的 则要退出 按 C
  • SQL Server学习之复合索引

    概要什么是单一索引 什么又是复合索引呢 何时新建复合索引 复合索引又需要注意些什么呢 本篇文章主要是对网上一些讨论的总结 一 概念单一索引是指索引列为一列的情况 即新建索引的语句只实施在一列上 用户可以在多个列上建立索引 这种索引叫做复合索
  • GPT系列模型技术路径演进

    目录 前言 Transformer GPT 1 BERT GPT 2 GPT 3 InstructGPT ChatGPT GPT 4 类ChatGPT产品 Google Bard 诗人 facebook LLaMA 羊驼 复旦 MOSS 清
  • 源码安装ODOO12

    要从源码安装 Odoo 我们首先要从 GitHub 上克隆一套 Odoo 源代码 mkdir home tderp conda envs odoo12 odoo dev 在odoo12虚环境下创建工作目录 cd home tderp con
  • linux怎么打开.o文件,Linux下文件I/O操作的相关知识

    Linux文件I O主要指的是文件的输入输出 很多初学者对文件的I O不是很了解 Linux文件I O的操作较多 下面小编就给大家详细介绍下Linux文件I O linux 文件I O教程 1 一 文件描述符 对内核而言 所以打开的文件都通
  • Android UI架构(十)--App请求切换帧率(1).md

    文章目录 参考资料 前言 背景 查看设备可支持的刷新率和分辨率 App设置设备帧率 Android Q及以下版本 Android R 参考资料 https zhuanlan zhihu com p 142212769 from voters
  • CC00041.bigdatajava——

    一 逻辑运算符概念使用 逻辑运算短路特性 逻辑运算符概念 gt 表示逻辑与运算符 相当于 并且 同真为真 一假为假 gt 表示逻辑或运算符 相当于 或者 一真为真 同假为假 gt 表示逻辑非运算符 相当于 取反 真为假 假为真 gt 逻辑运
  • ES(四)ES使用(基本查询、聚合查询)

    基本操作 操作索引 1 新建索引 curl XPUT localhost 9200 index01 2 查看索引 curl XGET http 192 168 168 101 9200 index01 settings curl XGET
  • Session和Cookie的用法及区别

    Session Cookie是什么 1 1 概念理解 要了解session和cookie是什么 先要了解以下几个概念 1 1 1 无状态的HTTP协议 协议 是指计算机通信网络中两台计算机之间进行通信所必须共同遵守的规定或规则 超文本传输协
  • 独立服务器和虚拟主机,浅谈独立服务器、云服务器、虚拟主机的区别

    当代的生活离不开网络的使用 不管是电商 游戏 还是视频行业 很多业务都需要依赖网络才得以开展 包括海外市场的推广与后期的联系巩固 真是巨大的市场需求 也使网络根据一些特性与使用衍生出了不同的类型 包括服务器 云服务器 虚拟主机等 那么 小编
  • 解决 k8s 集群1.26.3使用nfs时nfs provider selfLink was empty

    背景 1 使用1 26 3版本的集群 2 搭建nfs服务器 3 使用storageclass创建pvc时 一直pending 4 尝试修改vim etc kubernetes manifests kube apiserver yaml 文件
  • makefile工具的使用,编写一个或多个文件!(简单易上手)

    一 make 和 makefile 是什么 1 make 是一个 命令 2 makefile 是一个 文件 可以自动化的构建项目 3 编写 makefile 需要 1 依赖关系 2 依赖方法 二 什么是依赖关系 什么是依赖方法 下面我简单写
  • JUC学习系列五(ThreadLocal)

    该类提供了线程局部 thread local 变量 这些变量不同于它们的普通对应物 因为访问某个变量 通过其 get 或 set 方法 的每个线程都有自己的局部变量 它独立于变量的初始化副本 ThreadLocal 实例通常是类中的 pri