一、概述
ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是Java中一种较为特殊的线程绑定机制,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。
从线程的角度看,每个线程都保持一个对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。
通过ThreadLocal存取的数据,总是与当前线程相关,也就是说,JVM 为每个运行的线程,绑定了私有的本地实例存取空间,从而为多线程环境常出现的并发访问问题提供了一种隔离机制。
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。
概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
二、API说明
ThreadLocal()
创建一个线程本地变量。
T get()
返回此线程局部变量的当前线程副本中的值,如果这是线程第一次调用该方法,则创建并初始化此副本。
protected T initialValue()
返回此线程局部变量的当前线程的初始值。最多在每次访问线程来获得每个线程局部变量时调用此方法一次,即线程第一次使用 get() 方法访问变量的时候。如果线程先于 get 方法调用 set(T) 方法,则不会在线程中再调用 initialValue 方法。
若该实现只返回 null;如果程序员希望将线程局部变量初始化为 null 以外的某个值,则必须为 ThreadLocal 创建子类,并重写此方法。通常,将使用匿名内部类。initialValue 的典型实现将调用一个适当的构造方法,并返回新构造的对象。
void remove()
移除此线程局部变量的值。这可能有助于减少线程局部变量的存储需求。如果再次访问此线程局部变量,那么在默认情况下它将拥有其 initialValue。
void set(T value)
将此线程局部变量的当前线程副本中的值设置为指定值。许多应用程序不需要这项功能,它们只依赖于 initialValue() 方法来设置线程局部变量的值。
在程序中一般都重写initialValue方法,以给定一个特定的初始值。
三、典型实例
1、Hiberante的Session 工具类HibernateUtil
这个类是Hibernate官方文档中HibernateUtil类,用于session管理。
public class HibernateUtil { private static Log log = LogFactory.getLog(HibernateUtil.class); private static final SessionFactory sessionFactory; //定义SessionFactory static { try { // 通过默认配置文件hibernate.cfg.xml创建SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory(); } catch (Throwable ex) { log.error("初始化SessionFactory失败!", ex); throw new ExceptionInInitializerError(ex); } } //创建线程局部变量session,用来保存Hibernate的Session public static final ThreadLocal session = new ThreadLocal(); /** * 获取当前线程中的Session * @return Session * @throws HibernateException */ public static Session currentSession() throws HibernateException { Session s = (Session) session.get(); // 如果Session还没有打开,则新开一个Session if (s == null) { s = sessionFactory.openSession(); session.set(s); //将新开的Session保存到线程局部变量中 } return s; } public static void closeSession() throws HibernateException { //获取线程局部变量,并强制转换为Session类型 Session s = (Session) session.get(); session.set(null); if (s != null) s.close(); } }
在这个类中,由于没有重写ThreadLocal的initialValue()方法,则首次创建线程局部变量session其初始值为null,第一次调用currentSession()的时候,线程局部变量的get()方法也为null。因此,对session做了判断,如果为null,则新开一个Session,并保存到线程局部变量session中,这一步非常的关键,这也是“public static final ThreadLocal session = new ThreadLocal()”所创建对象session能强制转换为Hibernate Session对象的原因。
2、另外一个实例
创建一个Bean,通过不同的线程对象设置Bean属性,保证各个线程Bean对象的独立性。
/** * Created by IntelliJ IDEA. * User: leizhimin * Date: 2007-11-23 * Time: 10:45:02 * 学生 */ public class Student { private int age = 0; //年龄 public int getAge() { return this.age; } public void setAge(int age) { this.age = age; } }
/** * Created by IntelliJ IDEA. * User: leizhimin * Date: 2007-11-23 * Time: 10:53:33 * 多线程下测试程序 */ public class ThreadLocalDemo implements Runnable { //创建线程局部变量studentLocal,在后面你会发现用来保存Student对象 private final static ThreadLocal studentLocal = new ThreadLocal(); public static void main(String[] agrs) { ThreadLocalDemo td = new ThreadLocalDemo(); Thread t1 = new Thread(td, "a"); Thread t2 = new Thread(td, "b"); t1.start(); t2.start(); } public void run() { accessStudent(); } /** * 示例业务方法,用来测试 */ public void accessStudent() { //获取当前线程的名字 String currentThreadName = Thread.currentThread().getName(); System.out.println(currentThreadName + " is running!"); //产生一个随机数并打印 Random random = new Random(); int age = random.nextInt(100); System.out.println("thread " + currentThreadName + " set age to:" + age); //获取一个Student对象,并将随机数年龄插入到对象属性中 Student student = getStudent(); student.setAge(age); System.out.println("thread " + currentThreadName + " first read age is:" + student.getAge()); try { Thread.sleep(500); } catch (InterruptedException ex) { ex.printStackTrace(); } System.out.println("thread " + currentThreadName + " second read age is:" + student.getAge()); } protected Student getStudent() { //获取本地线程变量并强制转换为Student类型 Student student = (Student) studentLocal.get(); //线程首次执行此方法的时候,studentLocal.get()肯定为null if (student == null) { //创建一个Student对象,并保存到本地线程变量studentLocal中 student = new Student(); studentLocal.set(student); } return student; } }
运行结果:
a is running!
thread a set age to:76
b is running!
thread b set age to:27
thread a first read age is:76
thread b first read age is:27
thread a second read age is:76
thread b second read age is:27
thread a set age to:76
b is running!
thread b set age to:27
thread a first read age is:76
thread b first read age is:27
thread a second read age is:76
thread b second read age is:27
可以看到a、b两个线程age在不同时刻打印的值是完全相同的。这个程序通过妙用ThreadLocal,既实现多线程并发,游兼顾数据的安全性。
四、总结
ThreadLocal使用场合主要解决多线程中数据数据因并发产生不一致问题。ThreadLocal为每个线程的中并发访问的数据提供一个副本,通过访问副本来运行业务,这样的结果是耗费了内存,单大大减少了线程同步所带来性能消耗,也减少了线程并发控制的复杂度。
ThreadLocal不能使用原子类型,只能使用Object类型。ThreadLocal的使用比synchronized要简单得多。
ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。
Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
当然ThreadLocal并不能替代synchronized,它们处理不同的问题域。Synchronized用于实现同步机制,比ThreadLocal更加复杂。
五、ThreadLocal使用的一般步骤
1、在多线程的类(如ThreadDemo类)中,创建一个ThreadLocal对象threadXxx,用来保存线程间需要隔离处理的对象xxx。
2、在ThreadDemo类中,创建一个获取要隔离访问的数据的方法getXxx(),在方法中判断,若ThreadLocal对象为null时候,应该new()一个隔离访问类型的对象,并强制转换为要应用的类型。
3、在ThreadDemo类的run()方法中,通过getXxx()方法获取要操作的数据,这样可以保证每个线程对应一个数据对象,在任何时刻都操作的是这个对象。
参考文档:
JDK 官方文档
相关推荐
太赫兹金属回形结构:电磁波调控与信号传输的关键技术,太赫兹金属回形结构。 ,太赫兹; 金属; 回形结构; 电磁波响应,太赫兹金属回形结构:高效电磁波调控技术
路翼DCS460电脑调音软件下载是专为汽车音响爱好者和专业人士设计的一款强大工具, 这款软件的主要功能在于帮助用户对车载音频系统进行精确的数字信号处理,以提升音乐播放效果,提供更丰富的听觉体验。
基于Matlab的轴承故障分类系统:小波包能量特征提取与深度置信网络(DBN)的分类模型研究与应用,基于小波包能量特征提取和深度置信网络(DBN)的轴承故障分类 开发语言matlab 程序内容包括 1.轴承故障数据一份,共10类 2.数据读取,训练集,测试集数据划分。 3.小波包特征能量特征提取程序一份 4.基于DBN故障分类模型一份 ,小波包能量特征提取;DBN故障分类模型;Matlab;轴承故障数据;数据划分,基于MATLAB的轴承故障分类:小波包能量特征提取与深度置信网络分类模型
matlab实现PSO-BP分类完整程序+数据
基于AHP-CRITIC组合变权与指标劣化度修正的赋权方法研究,38考虑劣化度APH-CRITIC组合变权 组合变权赋权方法,基于AHP和改进CRITIC计算主客观权重,引入指标劣化度构造变权函数对综合权重进行修正,还方法可以捕捉指标时序的劣化程度,实现数据的有效跟踪,评价更加合理。 可根据需求进行改进。 ,关键词:组合变权赋权方法;AHP;CRITIC;指标劣化度;变权函数;时序劣化程度;数据跟踪;评价合理。,基于AHP-CRITIC组合变权法:综合主客观权重与指标劣化度评价
ROS机械臂仿真与视觉抓取技术:Darknet_ROS配置及Matlab运动学轨迹规划研究,ros机械臂仿真代做,视觉抓取,darknet_ros配置 Matlab机械臂运动学,轨迹规划 ,ROS机械臂仿真; 视觉抓取; darknet_ros配置; Matlab机械臂运动学; 轨迹规划,ROS机械臂仿真与视觉抓取:Darknet_ROS配置及Matlab运动学轨迹规划
农村事务管理与交流平台 免费JAVA毕业设计 2024成品源码+论文+录屏+启动教程 启动教程:https://www.bilibili.com/video/BV1jKDjYrEz1 项目讲解视频:https://www.bilibili.com/video/BV1Tb421n72S 二次开发教程:https://www.bilibili.com/video/BV18i421i7Dx
"基于Rsoft的光纤拉锥与弯曲模型仿真研究:探究beamprop模块的应用",光纤弯曲、拉锥弯曲模型仿真 Rsoft光学仿真,beamprop模块 ,光纤弯曲; 拉锥弯曲模型仿真; Rsoft光学仿真; beamprop模块,Rsoft仿真:光纤拉锥与弯曲的光束传播模型研究
亚像素提取的精确利器:Bresenham算法与卡尺算法的融合应用,bresenham算法,用于亚像素提取,卡尺算法 ,Bresenham算法; 亚像素提取; 卡尺算法,"Bresenham算法:亚像素提取的精准工具"
基于Vivado HLS的CLAHE算法FPGA实现:高效率视频处理IP核工程,限制对比度的自适应直方图均衡算法(CLAHE)的FPGA实现。 可实时处理视频流。 算法具体内容不做过多介绍,网上都有。 使用vivado hls实现,生成的IP核的输入输出接口都为axi-stream。 已经上板跑通(zynq7020)。 摄像头分辨率400*400-30fps,可以轻松的做到实时处理。 (如果您不清楚我的源码是否能应用到您的项目中,可以发我硬件平台和要处理视频流的分辨率与帧率,帮你评估。 )此hls源码工程。 ,关键词: 1. 限制对比度的自适应直方图均衡算法(CLAHE) 2. FPGA实现 3. 实时处理视频流 4. Vivado HLS 5. AXI-Stream接口 6. Zynq7020平台 7. 摄像头分辨率与帧率 8. HLS源码工程,基于Vivado HLS的CLAHE算法FPGA实现:实时视频流处理工程
games101-作业3
"基于Halcon的C#可视化工具:轻松抓边抓圆,Halcon控件上绘制更简单",使用C#新研发的基于Halcon的可视化抓边、抓圆工具,在Halcon控件上绘制的,使用起来简单 ,使用C#研发;Halcon可视化抓边工具;Halcon抓圆工具;在Halcon控件上绘制;简单易用;快速使用;直接绘制,"C#研发的Halcon可视化工具:抓边抓圆,简单易用"
基于微信小程序的校园食堂订餐服务系统 免费JAVA毕业设计 2024成品源码+论文+录屏+启动教程 启动教程:https://www.bilibili.com/video/BV1jKDjYrEz1 项目讲解视频:https://www.bilibili.com/video/BV1Tb421n72S 二次开发教程:https://www.bilibili.com/video/BV18i421i7Dx
"COMSOL PDE中设置Floquet周期性边界条件的步骤与注意事项",comsol pde设置floqeut周期性边界条件 ,comsol; pde设置; floqeut; 周期性边界条件,COMSOL PDE设置周期性边界条件
计算机网络第八版课件资料
基于FLAC3D的复杂地质环境下的双线隧道与基坑协同开挖策略:分步开挖,多层防护处理,flac3d 双线隧道开挖和基坑开挖。 临近既有隧道基坑开挖。 首先进行隧道开挖,考虑应力释放,使用反力支撑法,使用shell壳单元支护。 然后进行基坑开挖,使用地连墙和对撑支护。 分三层开挖。 ,flac3d;双线隧道开挖;基坑开挖;应力释放;反力支撑法;shell壳单元支护;分三层开挖;地连墙;对撑支护。,FLAC3D:隧道基坑双线开挖与支护技术
4b076399e3f709dc8990bd0e12720254.part6
西门子S7-200PLC与组态王技术在温室大棚系统中的应用与实现,38#西门子S7-200PLC和组态王温室大棚系统 ,38号项目; 西门子S7-200PLC; 组态王; 温室大棚系统; 控制系统,西门子S7-200PLC与组态王联控温室大棚系统
"深度探讨:对称修正梯形加速度规律插补算法的推导与仿真实践",对称修正梯形加速度规律插补算法推导仿真 ,对称修正; 梯形加速度; 插补算法; 推导; 仿真,对称梯形加速度插补算法推导仿真
"基于MATLAB GUI界面的多步骤裂缝检测系统:去除阴影,滤噪处理,图像增强与骨架特征提取技术",- 标题: 基于matlab的裂缝检测系统 - 关键词:matlab GUI界面 数字图像处理 去除阴影 滤波 图像增强 大津算法 otsu zhang_suen算法 形态学操作 骨架特征提取 中轴变化 - 步骤:打开图像 去除阴影 滤波操作 图像增强 阈值处理 形态学操作 骨架提取 - 简述:使用数字图像处理技术对输入图像去阴影操作,并可选择使用:中值滤波,均值滤波, 高斯滤波三种算法,之后再对图像进行增强操作,阈值化的大津算法,以及对阈值处理后的图像进行形态学操作,最终使用zhang_suen算法或者中轴变化算法,骨架效果提取明显,能保持裂缝的总体结构不变化且连续。 ,基于matlab的裂缝检测系统;matlab GUI界面;数字图像处理;去除阴影;滤波(中值滤波、均值滤波、高斯滤波);图像增强;大津算法;形态学操作;zhang_suen算法;骨架特征提取;中轴变化。,基于Matlab GUI的裂缝检测系统:数字图像处理与阴影去除技术