ThreadLocal与局部变量

2023-11-07

ThreadLocal和线程局部变量有什么区别,我们先看一段代码,如下:

public class ThreadLocalLearn {

	static ThreadLocal<IntHolder> tl = new ThreadLocal<IntHolder>(){

		protected IntHolder initialValue() {
			
			return new IntHolder();
		}
		
	};
	
	public static void main(String args[]) {
		
		for(int i=0; i<5; i++) {
			
			Thread th = new Thread(new ThreadTest(tl, i));
			th.start();
		}
	}
}

class ThreadTest implements Runnable{

	ThreadLocal<IntHolder> tl ; //threadlocal变量
	int i;
	int a = 3; //线程局部变量

	public ThreadTest(ThreadLocal<IntHolder> tl, int i) {
		super();
		this.tl = tl;
		this.i = i;
	}

	@Override
	public void run() {
		
		tl.get().increAndGet();
		a++;
		System.out.println(tl.get().getA() + " ");
		System.out.println("a : " + a);
	}
	
}

class IntHolder{
	
	int a = 1;

	public int getA() {
		return a;
	}

	public void setA(int a) {
		this.a = a;
	}
	
	public int increAndGet() {
		
		return ++a;
	}
}

代码的运行结果如下:

2 
a : 4
2 
a : 4
2 
a : 4
2 
a : 4
2 
a : 4

可以看到,局部变量和ThreadLocal起到的作用是一样的,保证了并发环境下数据的安全性。那就是说,完全可以用局部变量来代替ThreadLocal咯,这样想法对么?我们看一看官方对于ThreadLocal的描述:

This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its {@code get} or {@code set} method) has its own, independently initialized copy of the variable. {@code ThreadLocal} instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).

翻译起来就是

ThreadLocal提供的是一种线程局部变量。这些变量不同于其它变量的点在于每个线程在获取变量的时候,都拥有它自己相对独立的变量初始化拷贝。ThreadL:ocal的实例一般是私有静态的,可以做到与一个线程绑定某一种状态。PS:有更好的翻译请指教。

      所以就这段话而言,我们知道ThreadLocal不是为了满足多线程安全而开发出来的,因为局部变量已经足够安全。ThreadLocal是为了方便线程处理自己的某种状态。
      可以看到ThreadLocal实例化所处的位置,是一个线程共有区域。好比一个银行和个人,我们可以把钱存在银行,也可以把钱存在家。存在家里的钱是局部变量,仅供个人使用;存在银行里的钱也不是说可以让别人随便使用,只有我们以个人身份去获取才能得到。所以说ThreadLocal封装的变量我们是在外面某个区域保存了处于我们个人的一个状态,只允许我们自己去访问和修改的状态。
      ThreadLocal同时提供了初始化的机制,在实例化时重写initialValue()方法,便可实现变量的初始化工作

	//method 1
	static ThreadLocal<IntHolder> tl = new ThreadLocal<IntHolder>(){

		protected IntHolder initialValue() {
			
			return new IntHolder();
		}
		
	};
	//method 2
	IntHolder  intHolder = new IntHolder();
	static ThreadLocal<IntHolder> tl = new ThreadLocal<IntHolder>(){

		protected IntHolder initialValue() {
			
			return intHolder;
		}
		
	};

      方法一和方法二都可以实现初始化工作,但是方法二不能保证线程变量的安全性,因为引用拷贝指向的是同一个实例,对引用拷贝的修改,等同于对实例的修改。

方法二的样例
方法一的样例

      当然,也可以在判断ThreadlLocal获取数据为空时,在线程内部为ThreadLocal实例化一个数据。如下:

if(null == tl.get()) {
			
	tl.set(new IntHolder());
}

      为什么我们需要ThreadLocal来实现在自身内部外创建一个有关自己的状态呢?其实可以完全使用参数传递内部参数,就像我在自己随便放钱一样!这里要注意的是,我们定义一个方法的时候,并不是参数越多越好,有些共有参数,我们应该尽量设为全局,便于系统的可维护性与可扩展性。另一个角度考虑,银行也有其存在的价值,ThreadLocal会简化我们的编程,毕竟它是安全的。

根据ThreadLocal原理,我们自己实现一个:

import java.util.concurrent.ConcurrentHashMap;

public class ThreadLocalVar<T> {

	public T initVaribale() {
		
		return null;
	}
	
	volatile ConcurrentHashMap<Thread, T> map = new ConcurrentHashMap<Thread, T>(); 
	
	public void put(T t) {
		
		map.put(Thread.currentThread(), t);
	}
	
	public T get() {
		
		T value = map.get(Thread.currentThread());
		if(null == value) {
			
			T t = initVaribale();
			if(null == t) {
				return null;
			}
			map.put(Thread.currentThread(), t);
			return t;
		} else {
			
			return value;
		}
	}
}

有兴趣也可以去阅读jdk中ThreadLocal的源码~

===================
后记:20190622
`

局部变量可以保证的是方法内的线程私有,threadlocal可以保证对象级别的线程私有特性。因此局部变量在某些情况下不能保证并发安全性

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

ThreadLocal与局部变量 的相关文章

  • Mockito:如何通过模拟测试我的服务?

    我是模拟测试新手 我想测试我的服务方法CorrectionService correctPerson Long personId 实现尚未编写 但这就是它将执行的操作 CorrectionService将调用一个方法AddressDAO这将
  • Spring AspectJ 在双代理接口时失败:无法生成类的 CGLIB 子类

    我正在使用Spring的
  • 过滤两次 Lambda Java

    我有一个清单如下 1 2 3 4 5 6 7 和 预期结果必须是 1 2 3 4 5 6 7 我知道怎么做才能到7点 我的结果 1 2 3 4 5 6 我也想知道如何输入 7 我添加了i gt i objList size 1到我的过滤器
  • 在接口中使用默认方法是否违反接口隔离原则?

    我正在学习 SOLID 原则 ISP 指出 客户端不应被迫依赖于他们所使用的接口 不使用 在接口中使用默认方法是否违反了这个原则 我见过类似的问题 但我在这里发布了一个示例 以便更清楚地了解我的示例是否违反了 ISP 假设我有这个例子 pu
  • 来自 dll 的 Java 调用函数

    我有这个 python 脚本导入zkemkeeperdll 并连接到考勤设备 ZKTeco 这是我正在使用的脚本 from win32com client import Dispatch zk Dispatch zkemkeeper ZKE
  • 从最终实体获取根证书和中间证书

    作为密码学的菜鸟 我每天都会偶然发现一些简单的事情 今天只是那些日子之一 我想用 bouncy castle 库验证 java 中的 smime 消息 我想我几乎已经弄清楚了 但此时的问题是 PKIXparameters 对象的构建 假设我
  • 没有 Spring 的自定义 Prometheus 指标

    我需要为 Web 应用程序提供自定义指标 问题是我不能使用 Spring 但我必须使用 jax rs 端点 要求非常简单 想象一下 您有一个包含键值对的映射 其中键是指标名称 值是一个简单的整数 它是一个计数器 代码会是这样的 public
  • 无法创建请求的服务[org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]-MySQL

    我是 Hibernate 的新手 我目前正在使用 Spring boot 框架并尝试通过 hibernate 创建数据库表 我知道以前也问过同样的问题 但我似乎无法根据我的环境找出如何修复错误 休眠配置文件
  • 将 MOXy 设置为 JAXB 提供程序,而在同一包中没有属性文件

    我正在尝试使用 MOXy 作为我的 JAXB 提供程序 以便将内容编组 解组到 XML JSON 中 我创建了 jaxb properties 文件 内容如下 javax xml bind context factory org eclip
  • 帮助将图像从 Servlet 获取到 JSP 页面 [重复]

    这个问题在这里已经有答案了 我目前必须生成一个显示字符串文本的图像 我需要在 Servlet 上制作此图像 然后以某种方式将图像传递到 JSP 页面 以便它可以显示它 我试图避免保存图像 而是以某种方式将图像流式传输到 JSP 自从我开始寻
  • 如何在用户输入数据后重新运行java代码

    嘿 我有一个基本的java 应用程序 显示人们是成年人还是青少年等 我从java开始 在用户输入年龄和字符串后我找不到如何制作它它们被归类为 我希望它重新运行整个过程 以便其他人可以尝试 的节目 我一直在考虑做一个循环 但这对我来说没有用
  • java.io.Serialized 在 C/C++ 中的等价物是什么?

    C C 的等价物是什么java io Serialized https docs oracle com javase 7 docs api java io Serializable html 有对序列化库的引用 用 C 序列化数据结构 ht
  • 专门针对 JSP 的测试驱动开发

    在理解 TDD 到底是什么之前 我就已经开始编写测试驱动的代码了 在没有实现的情况下调用函数和类可以帮助我以更快 更有效的方式理解和构建我的应用程序 所以我非常习惯编写代码 gt 编译它 gt 看到它失败 gt 通过构建其实现来修复它的过程
  • 干净构建 Java 命令行

    我正在使用命令行编译使用 eclipse 编写的项目 如下所示 javac file java 然后运行 java file args here 我将如何运行干净的构建或编译 每当我重新编译时 除非删除所有内容 否则更改不会受到影响 cla
  • C#:帮助理解 UML 类图中的 <>

    我目前正在做一个项目 我们必须从 UML 图编写代码 我了解 UML 类图的剖析 但我无法理解什么 lt
  • 如何使用mockito模拟构建器

    我有一个建造者 class Builder private String name private String address public Builder setName String name this name name retur
  • 使用反射覆盖最终静态字段是否有限制?

    在我的一些单元测试中 我在最终静态字段上的反射中遇到了奇怪的行为 下面是说明我的问题的示例 我有一个基本的 Singleton 类 其中包含一个 Integer public class BasicHolder private static
  • 长轮询会冻结浏览器并阻止其他 ajax 请求

    我正在尝试在我的中实现长轮询Spring MVC Web 应用程序 http static springsource org spring docs 2 0 x reference mvc html但在 4 5 个连续 AJAX 请求后它会
  • 使用 svn 1.8.x、subclise 1.10 的 m2e-subclipse 连接器在哪里?

    我读到 m2e 的生产商已经停止生产 svn 1 7 以外的任何版本的 m2e 连接器 Tigris 显然已经填补了维护 m2e subclipse 连接器的空缺 Q1 我的问题是 使用 svn 1 8 x 的 eclipse 更新 url
  • 如何防止在Spring Boot单元测试中执行import.sql

    我的类路径中有一个 import sql 文件 其中包含一些 INSERT 语句 当使用 profile devel 运行我的应用程序时 它的数据被加载到 postgres 数据库中 到目前为止一切正常 当使用测试配置文件执行测试时 imp

随机推荐

  • 2023华为OD机试真题【连接器/贪心算法】

    题目描述 有一组区间 a0 b0 a1 b1 a b表示起点 终点 区间有可能重叠 相邻 重叠或相邻则可以合并为更大的区间 给定一组连接器 x1 x2 x3 x表示连接器的最大可连接长度 即x gt gap 可用于将分离的区间连接起来 但两
  • linux搭建环境命令,在Linux上搭建测试环境常用命令(转自-测试小柚子)

    一 搭建测试环境 二 查看应用日志 1 vi vi vim 原本是指修改文件 同时可以使用vi 日志文件名 打开日志文件 2 less less命令是查看日志最常用的命令 用法 less 日志文件名 分页显示文件的内容 经常使用这个命令是因
  • [开发中遇到的算法] 均分数组

    业务背景 最近我需要写并发rpc的负载均衡 某种意义上的吧 遇到很有意思的问题 需求如下 下游固定死最多一次请求100个 比如要请求101个时要拆两个请求并发rpc 并等待两个请求都返回后拼装成一个结果返回 拆成51个 50个发出请求比拆成
  • lgg8各个版本_如何评价LG G8?

    回复下吧 产品中规中矩的升级 奈何同期对手太强 宣发脑子被驴踢 前置TOF早有透露 自家lg innotek的产品 效果不错 能更好3d人脸自拍 人脸识别 以及AR 都9012了搞隔空操作还作为宣传主力真是脑子进了水 忘了三星S4的眼球操作
  • [人工智能-深度学习-24]:卷积神经网络CNN - CS231n解读 - 卷积神经网络基本层级

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 人工智能 深度学习 23 卷积神经网络CNN CS231n解读 卷积神经网络基本层级 文火冰糖 王文兵 的博客 CSDN博客 目录 第1章
  • 使用elment+moment写年时间段选择

    要求 选择年的时间段 不能选择当前年之后的年份 先看实现效果如 1 html结构代码
  • 矩阵的迹(Trace)

    译自维基百科 在线性代数中 方阵A n n 的迹定义为对角线元素的和 即 矩阵的迹表示的是特征值的和 它不随基的变化而变化 通常 这种特性可以用来定义线性算子的轨迹 注意 迹是对方阵而言的 举例 A是一个方阵 如下 则A的迹表示为 迹的特性
  • KEIL编译出现错误“source file is not valid utf-8”

    KEIL编译出现错误 source file is not valid utf 8 在外面复制了一段代码 c文件一直报错source file is not valid utf 8的错误 经查找原因就是 文件中出现中文符号导致的 特别是中文
  • 用Excel做相关性分析

    一 概念理解 相关关系 变量之间存在着的非严格的不确定的关系 对它们进行深层次的分析 观察它们的密切程度 相关性分析 对变量之间相关关系的分析 即相关性分析 其中比较常用的是线性相关分析 用来衡量它的指标是线性相关系数 又叫皮尔逊相关系数
  • new的三种用法

    new的三种用法 第一种 创建一个新对象 Test p new Test 10 这里的new的用法是创建一个新的Test型的对象 该用法一共有三个步骤 1 申请一个空间 2 在申请的空间当中构造一个对象 并将该对象放置到空间中 3 将空间的
  • Ubuntu下NFS服务器配置及应用

    NFS文件系统仅占用系统挂载点 NFS服务器设定好分享的目录 home shares 其他客服端就可以将这个目录挂载到自己系统上的挂载点上 home shares就像自己的一个分区 但不占用自己的磁盘空间 虽然NFS有自己的协议及端口号 但
  • 必测的支付漏洞(一)——使用fiddler篡改支付金额

    互联网产品中常会遇到支付功能 测试人员测试这部分功能时一定要重视 因为如果这部分出现了较严重的bug 将会给公司带来不小的经济损失 如果你测出了问题领导也一定会高兴的 因此测试优先级很高 但具有一定难度 刚接触测试的小白们可能不知道支付功能
  • 五分钟成为记忆王

    一 记忆的面纱 1 记忆的含义 1 就在我嘴边上 有多少次你这样说过 就在我嘴边上 又有过多少次在你需要什么时候 任凭你如何拼命地想 就是想不起来 当然 这问题不是你一个人才有 几乎所有的人都受到过记忆力差的困扰 这也是人类的一个最常见的不
  • stm32 IO口的八种输入输出模式

    记录一下stm32 IO口的八种输入输出模式的学习 首先 可以看见stm32的输入输出模式有以上8种 先从简单的开始说吧 上拉输入和下拉输入 看图 由上图可见 当IO口设置为上拉输入的时候 IO口内部的上拉电阻就被接上了 从字面意思可以理解
  • Java异常总结

    1 异常的定义 定义 异常又称例外 是程序执行过程中发生的事件 它会终止程序的正常执行 2 异常的分类 Error 是JVM内部产生的 不需要程序员去解决 是不受检查异常 非代码性错误 Exception 是用户程序可能出现的异常 它是用来
  • 单片机关于推挽输出和开漏输出

    什么是推挽输出 推挽输出既可以输出高电平也可以输出低电平 推挽式输出电路 推挽式输出电路是由互补的两个三极管构成 所谓推拉 推是指推出去 就是输出为高电平是 电流是由内流向外的 形象的称之为推 拉就是从外部向内部拉 当输出为低电平时 电流由
  • java设计模式——享元模式(Flyweight Pattern)

    概述 面向对象技术可以很好地解决一些灵活性或可扩展性问题 但在很多情况下需要在系统中增加类和对象的个数 当对象数量太多时 将导致运行代价过高 带来性能下降等问题 享元模式正是为解决这一类问题而诞生的 享元模式通过共享技术实现相同或相似对象的
  • java 抓取网页_Java抓取网页数据

    有时候由于种种原因 我们需要采集某个网站的数据 但由于不同网站对数据的显示方式略有不同 本文就用Java给大家演示如何抓取网站的数据 1 抓取原网页数据 2 抓取网页JavaScript返回的数据 一 抓取原网页 这个例子我们准备从http
  • 基于Cordova插件创建app及打包成apk

    基于Cordova插件创建app及打包成apk 1 配置开发环境 一 下载并安装node js npm功能可以使用 二 利用npm安装cordova插件 三 配置JAVA ANDROID GRDLE的系统环境 1 java jdk jre设
  • ThreadLocal与局部变量

    ThreadLocal和线程局部变量有什么区别 我们先看一段代码 如下 public class ThreadLocalLearn static ThreadLocal