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与局部变量 的相关文章

  • 获取文件的锁

    我想在对特定文件开始 threo read 时获取文件上的锁定 以便其他应用程序无法读取已锁定的文件并希望在线程终止时释放锁定文件 您可以获得一个FileLock https docs oracle com javase 8 docs ap
  • 带有 Android 支持库 v7 的 Maven Android 插件

    我使用 maven android plugin 构建我的 android 应用程序 它依赖于 android 支持库 v4 和 v7 由于我没有找到如何从developer android com下载整个sdk 因此我无法使用maven
  • Java 7 默认语言环境

    我刚刚安装了 jre7 我很惊讶地发现我的默认区域设置现在是 en US 对于jre6 它是de CH 与jre7有什么不同 默认区域设置不再是操作系统之一吗 顺便说一句 我使用的是Windows7 谢谢你的回答 编辑 我已经看到了语言环境
  • 埃拉托色尼筛法 - 实现返回一些非质数值?

    我用 Java 实现了埃拉托斯特尼筛法 通过伪代码 public static void sieveofEratosthenes int n boolean numArray numArray new boolean n for int i
  • 是否可以从 servlet 内部以编程方式设置请求上下文路径?

    这是一个特殊情况 我陷入了处理 企业 网络应用程序的困境 企业应用程序正在调用request getContext 并将其与另一个字符串进行比较 我发现我可以使用 getServletContext getContextPath 获取 se
  • 如何使用正则表达式验证 1-99 范围?

    我需要验证一些用户输入 以确保输入的数字在 1 99 范围内 含 这些必须是整数 Integer 值 允许前面加 0 但可选 有效值 1 01 10 99 09 无效值 0 007 100 10 5 010 到目前为止 我已经制定了以下正则
  • 如何通过注解用try-catch包装方法?

    如果应该在方法调用中忽略异常 则可以编写以下内容 public void addEntryIfPresent String key Dto dto try Map
  • 虽然我的类已加载,但 Class.forName 抛出 ClassNotFoundException

    代码如下 它的作用是加载我放在主目录中的 jar 文件中的所有类 import java io File import java util jar JarFile import java util jar JarEntry import j
  • 如何删除日期对象的亚秒部分

    当 SQL 数据类型为时间戳时 java util Date 存储为 2010 09 03 15 33 22 246 如何在存储记录之前将亚秒设置为零 例如 在本例中为 246 最简单的方法是这样的 long time date getTi
  • 避免 Java 中的重复导入:继承导入?

    有没有办法 继承 导入 Example 常见枚举 public enum Constant ONE TWO THREE 使用此枚举的基类 public class Base protected void register Constant
  • 如何让 Emma 或 Cobertura 与 Maven 一起报告其他模块中源代码的覆盖率?

    我有一个带有 Java 代码的多模块 Maven 设置 我的单元测试在其中一个模块中测试多个模块中的代码 当然 这些模块具有相互依赖性 并且在测试执行之前根据需要编译所有相关模块中的代码 那么 如何获得整个代码库覆盖率的报告 注意 我不是问
  • 如何在Java中对对象数组进行字段级别排序以进行等级比较?

    In Java Class StudentProgress String Name String Grade CTOR goes here main class main method StudentProgress arrayofObje
  • 如何处理 StaleElementReferenceException

    我正在为鼠标悬停工作 我想通过使用 for 循环单击每个链接来测试所有链接的工作条件 在我的程序中 迭代进行一次 而对于下一次迭代 它不起作用并显示 StaleElementReferenceException 如果需要 请修改代码 pub
  • 替换文件中的字符串

    我正在寻找一种方法来替换文件中的字符串而不将整个文件读入内存 通常我会使用 Reader 和 Writer 即如下所示 public static void replace String oldstring String newstring
  • JMS 中的 MessageListener 和 Consumer 有什么区别?

    我是新来的JMS 据我了解Consumers能够从队列 主题中挑选消息 那么为什么你需要一个MessageListener因为Consumers会知道他们什么时候收到消息吗 这样的实际用途是什么MessageListener 编辑 来自Me
  • ECDH使用Android KeyStore生成私钥

    我正在尝试使用 Android KeyStore Provider 生成的私有文件在 Android 中实现 ECDH public byte ecdh PublicKey otherPubKey throws Exception try
  • Java 的 PriorityQueue 与最小堆有何不同?

    他们为什么命名PriorityQueue如果你不能插入优先级 它看起来与堆非常相似 有什么区别吗 如果没有区别那为什么叫它PriorityQueue而不是堆 默认的PriorityQueue是用Min Heap实现的 即栈顶元素是堆中最小的
  • HttpClient请求设置属性问题

    我使用这个 HttpClient 库玩了一段时间 几周 我想以某种方式将属性设置为请求 不是参数而是属性 在我的 servlet 中 我想使用 Integer inte Integer request getAttribute obj 我不
  • 如何使用通配符模拟泛型方法的行为

    我正在使用 EasyMock 3 2 我想基于 Spring Security 为我的部分安全系统编写一个测试 我想嘲笑Authentication http docs spring io autorepo docs spring secu
  • 即使调整大小,如何获得屏幕的精确中间位置

    好的 这个问题有两部分 当我做一个JFrame 并在其上画一些东西 即使我将宽度设置为 400 并使其在一个项目击中它时 当然 允许项目宽度 它会反弹回来 但由于某种原因 它总是偏离屏幕约 10 个像素 有没有办法解决这个问题 或者我只需要

随机推荐

  • 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