深入理解Threadlocal

  SUN公司早在JDK1.2的时候就为我们提供了java.lang.ThreadLocal,低版本的JDK所提供的get()返回的是Object对象,需要强制类型转换,使用起来不方便,而在JDK1.5引入了泛型,在一定程度地简化ThreadLocal的使用

  我们知道在spring容器中获取实例的时候都是默认单例模式,这在多线程开发的时候是尤其要注意的地方,下面先简单总结一下在开发中都有哪些情况需要考虑线程安全问题。

  1)常量始终是线程安全的,它的值在初始化之后不可以改变,所以只存在读操作。
  2)每次调用方法前都新建一个实例是线程安全的,因为不会访问共享的资源。
  3)局部变量是线程安全的。因为每执行一个方法,都会在独立的栈空间创建局部变量,它不是共享的资源。局部变量包括方法的参数变量和方法内变量。
  4)无状态的bean是线程安全的,所谓无状态就是一次操作,不能保存数据。无状态对象(Stateless Bean),就是没有实例变量的对象,不能保存数据,是不变类,是线程安全的。无状态的Bean适合用不变模式,技术就是单例模式,这样可以共享实例,提高性能。

  5)有状态的Bean,多线程环境下不安全。所谓有状态就是有数据存储功能,有状态对象(Stateful Bean),就是有实例变量的对象,可以保存数据,是非线程安全的。有状态的bean适合用Prototype原型模式。Prototype: 每次对bean的请求都会创建一个新的bean实例。

  6)静态变量在多线程环境下不安全。

  总的来说,线程安全问题都是由全局变量及静态变量引起的。 若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。Struts2默认的实现是Prototype模式。也就是每个请求都新生成一个bean实例,所以不存在线程安全问题。需要注意的是,如果由Spring管理bean的生命周期, scope要配成prototype作用域,同时在每次获取bean时都要从上下文对象中去取,而不仅仅是在配置文件中将scope配置成prototype。

  通常来讲当我们遇到线程安全问题,第一反应就是采用同步机制synchronize,通过对象的锁机制保证同一时间只有一个线程访问变量,这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。

  下面让我们打开源码,看一下ThreadLocal是怎样实现多线程环境下变量共享,首先看ThreadLocal接口:

  它主要有下4个方法,void set(Object value)设置当前线程的线程局部变量的值;public Object get()该方法返回当前线程所对应的线程局部变量;public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度;protected Object initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。

  

 

  

  我们仔细来看其set方法的实现,获取当前线程,根据线程获取一个ThreadLocalMap(ThreadLocal的一个静态内部类)对象。如果存在,则set进去一组值,key就是当前的ThreadLocal对象,value就是当前线程下该变量副本的值;如果不存在则创建一个ThreadLocalMap对象,我们点开creatMap方法发现,它会创建一个新的ThreadLocalMap对象,并且把当前ThreadLocal对象作为key,把初始化的值作为value放进去,然后赋值给当前线程的threadLocals属性,它的类型是ThreadLocalMap。

  由此我们可以得知每个线程都有一个ThreadLocalMap类型的属性threadLocals,它的初始值为null,当我们第一次在线程中调用ThreadLocal的set方法时,他会根据线程去找对应的ThreadLocalMap对象,此时对象为null,则创建一个新的ThreadLocalMap对象(初始key当前threadLocal对象,初始value当前threadLocal对象执行initialValue()方法后的值),并把它赋值给threadLocals,当第二次再调用ThreadLocal的set方法时则直接给当前线程的threadLocals属性里面的value值覆盖掉,因为key值始终不变,当第一次调用ThreadLocal的get方法时,首先获取当前线程,根据线程找到对应的ThreadLocalMap对象,然后根据当前的ThreadLocal对象去获取对应的value值。总的来说就是每个线程都有自己的ThreadLocalMap类型的一个属性,它是一个集合,里面的key类型为ThreadLocal,value就是各个ThreadLocal对象的变量副本的值,一个线程中有几个ThreadLocal类型的对象,那么这个线程的属性threadLocals就有几组值。

  下面我们通过实例来演示一下使用ThreadLocal和不使用的区别,代码如下:

  

  

  

  我们可以看到使用ThreadLocal后,我们有状态的bean在多线程环境下可以正确的显示出结果,不互相影响,当我们不用ThreadLocal将全局变量包裹时,修改TestThreadLocal类,测试结果如下:

  

    

  此时如果有过个线程同时对一个单例对象进行修改操作时,就会出现线程安全问题。

  ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。

转载于:https://www.cnblogs.com/hhhshct/p/6938003.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/400815.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HDU 2289 几何+圆台

题意;给定一个圆台杯子的R&#xff0c;r&#xff0c;H和里面水的体积V 求h 二分。。。。。。。。。。。。。。。。。 因为解不出来h。。。。。。。。。。。。 View Code 1 /*2 几何3 圆台体积4 V1/3*pi*h*(r1*r1r2*r2r1*r2)5 6 */7 #include<stdio.h>8 #include<stri…

MSP430F5529 DriverLib 库函数学习笔记(十)SPI驱动墨水屏

目录上机实战SPI 驱动 墨水屏墨水屏介绍电子纸的分类电泳型电子纸技术详解原理结构优势与不足实验电路介绍程序分析引脚初始化SPI模块初始化发送和接收全部源代码main.cmain.h墨水屏相关Paper_Display.cPaper_Display.hInclude.h实验结果现象波形对应平台&#xff1a;Code Comp…

Xamarin开发IOS笔记:切换输入法时输入框被遮住

在进行IOS开发的过程中&#xff0c;出现类似微信朋友圈的交互界面&#xff0c;当用户遇到感兴趣的内容可以进行评论。为了方便评论输入&#xff0c;当出现评论输入框的时候自动将评论输入框移动至键盘的上方&#xff0c;这样方便边输入边查看。 当用户隐藏键盘或者切换输入法的…

技术管理进阶——你了解成长的全貌吗?

&#x1f680; 优质资源分享 &#x1f680; 学习路线指引&#xff08;点击解锁&#xff09;知识定位人群定位&#x1f9e1; Python实战微信订餐小程序 &#x1f9e1;进阶级本课程是python flask微信小程序的完美结合&#xff0c;从项目搭建到腾讯云部署上线&#xff0c;打造一…

MSP430F5529 DriverLib 库函数学习笔记(十一)I2C / IIC

目录硬知识USCI的I2C模式I2C概述MSP430单片机I2C模块特征及结构框图I2C原理I2C主从操作&#xff08;1&#xff09;从模式&#xff08;2&#xff09;主模式I2C模式下的USCI中断USCI寄存器—I2C模式I2C API (机翻)API介绍主机模式主机模式单字节传输主机模式多字节传输主机模式单…

使用 Abp.Zero 搭建第三方登录模块(一):原理篇

&#x1f680; 优质资源分享 &#x1f680; 学习路线指引&#xff08;点击解锁&#xff09;知识定位人群定位&#x1f9e1; Python实战微信订餐小程序 &#x1f9e1;进阶级本课程是python flask微信小程序的完美结合&#xff0c;从项目搭建到腾讯云部署上线&#xff0c;打造一…

MSP430F5529 DriverLib 库函数学习笔记(十二)I2C实战

目录上机实战I2C给 DAC 芯片 DAC7571 写入数字量DAC7571 介绍程序分析引脚复用I2C 初始化发送一个字节发送一个字读取一个字节读取多个字节中断服务函数整体代码main.cMSP430F5529_I2C.cMSP430F5529_I2C.h实验结果I2C 读取 TMP421 温度TMP421 简介程序摘要TMP421初始化温度的读…

mysqldump造成Buffer Pool污染的研究 [转]

原文链接&#xff1a;http://www.shaoqun.com/m/a/43307.aspx 前言&#xff1a; 最近Oracle MySQL在其官方Blog上贴出了 5.6中一些变量默认值的修改。其中innodb_old_blocks_time 的默认值从0替换成了1000&#xff08;即1s&#xff09; 关于该参数的作用摘录如下&#xff1a; h…

自动加载缓存框架

2019独角兽企业重金招聘Python工程师标准>>> 自动加载缓存框架 代码&#xff0c;请访问github 获取更详情&#xff0c;更新的内容 QQ交流群:429274886&#xff0c;版本更新会在群里通知&#xff0c;能了解最新动态 0.5版本已经是稳定版本了&#xff0c;大家可以放心…

(数据科学学习手札139)geopandas 0.11版本重要新特性一览

&#x1f680; 优质资源分享 &#x1f680; 学习路线指引&#xff08;点击解锁&#xff09;知识定位人群定位&#x1f9e1; Python实战微信订餐小程序 &#x1f9e1;进阶级本课程是python flask微信小程序的完美结合&#xff0c;从项目搭建到腾讯云部署上线&#xff0c;打造一…

MSP430F5529 DriverLib 库函数学习笔记(十三)认识低功耗模式

目录硬知识低功耗模式MSP430单片机各工作模式下的电流消耗API进入低功耗模式退出低功耗模式平台&#xff1a;Code Composer Studio 10.3.1 MSP430F5529 LaunchPad™ Development Kit (MSP‑EXP430F5529LP) 硬知识 低功耗模式 MSP430单片机具有7种低功耗模式&#xff08;LPM0…

剖析虚幻渲染体系(16)- 图形驱动的秘密

&#x1f680; 优质资源分享 &#x1f680; 学习路线指引&#xff08;点击解锁&#xff09;知识定位人群定位&#x1f9e1; Python实战微信订餐小程序 &#x1f9e1;进阶级本课程是python flask微信小程序的完美结合&#xff0c;从项目搭建到腾讯云部署上线&#xff0c;打造一…

Python NLPIR(中科院汉语分词系统)的使用 十五分钟快速入门与完全掌握

前叙 博主写这篇文章使用了八十分钟,阅读需要十五分钟,读完之后你将会学会在Python中使用NLPIR2016.如果你花费更多的时间练习后半部分的例子,你将能够在一天内学会在Python中使用NLPIR2016的全部所需知识 如果你想要获取更详细的API翻译,你需要进一步学习ctypes,附赠一篇关于…

微信朋友圈也可以发语音你们造吗?

在微信朋友圈发照片太矫情&#xff0c;小视频又耗流量&#xff0c;只发文字又太单调&#xff0c;何不发段语音来呼朋唤友呢&#xff1f;你是说朋友圈发语音&#xff1f;这是真的吗&#xff1f;sure&#xff0c;开发者已经利用微信JS-SDK接口实现了&#xff0c;扫描下方的二维码…

MSP430F5529 DriverLib 库函数学习笔记(十四)看门狗定时器 (WDT)

目录硬知识看门狗定时计数器 &#xff08;WDTCNT&#xff09;看门狗模式定时计数模式看门狗定时器中断时钟故障保护功能低功耗模式下的看门狗操作看门狗定时器控制寄存器WDT_A API (机翻)参数上机实战定时计数模式配置中断服务函数整体代码实验结果看门狗模式配置整体代码平台&…

初学 Unsupervised feature learning and deep learning--Sparse autoencoder

今天做了一下 Stanford CS294A 的一个 programming assignment: sparse autoencoder 因为之前做过 Andrew Ng 的 ml online class 的 neural network 那节的作业&#xff0c;所以这个实现起来就很 easy 了。直接贴代码&#xff08;all vectorized&#xff09;&#xff1a;1 [d …

【Nginx】Windows平台下配置Nginx服务实现负载均衡

&#x1f680; 优质资源分享 &#x1f680; 学习路线指引&#xff08;点击解锁&#xff09;知识定位人群定位&#x1f9e1; Python实战微信订餐小程序 &#x1f9e1;进阶级本课程是python flask微信小程序的完美结合&#xff0c;从项目搭建到腾讯云部署上线&#xff0c;打造一…

PLC基础入门

PLC编程入门基础技术知识学习 2016-06-27 xjp7879 摘自 电工技术知...第一章 可编程控制器简介 可编程序控制器&#xff0c;英文称Programmable Controller&#xff0c;简称PC。但由于PC容易和个人计算机&#xff08;Personal Computer&#xff09;混淆&#xff0c;故人们仍习…

Xcode 的正确打开方式——Debugging

2019独角兽企业重金招聘Python工程师标准>>> 程序员日常开发中有大量时间都会花费在 debug 上&#xff0c;从事 iOS 开发不可避免地需要使用 Xcode。这篇博客就主要介绍了 Xcode 中几种能够大幅提升代码调试效率的方式。 “If debugging is the process of removing…

VS Code 调试 PHP有关配置

一、安装VS CODE 、 WampServer 二、用VS Code 打开php项目后&#xff0c;提示 "Cannot validate since no PHP executable is set. Use the setting php.validate.executablePath to configure the PHP executable." 解决方法如下&#xff1a; 在“文件”-“首选项”…