一、Spring单例模式及线程安全

  Spring框架中的Bean,或者说组件,获取实例的时候都是默认单例模式,这是在多线程开发的时候需要尤其注意的地方。

  单例模式的意思是只有一个实例,例如在Spring容器中某一个类只有一个实例,而且自行实例化后并项整个系统提供这个实例,这个类称为单例类。

  当多个用户同时请求一个服务时,容器会给每一个请求分配一个线程,这时多个线程会并发执行该请求对应的业务逻辑(成员方法),此时就要注意了,如果该处理逻辑中有对单例状态的修改(体现为该单例的成员属性),则必须考虑线程同步问题。

同步机制的比较:

  ThreadLocal和线程同步机制相比有什么优势呢?他们都是为了解决多线程中相同变量的访问冲突问题。

在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。 
而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。 
由于ThreadLocal中可以持有任何类型的对象,低版本JDK所提供的get()返回的是Object对象,需要强制类型转换。但JDK 5.0通过泛型很好的解决了这个问题,在一定程度地简化ThreadLocal的使用
概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。 
Spring使用ThreadLocal解决线程安全问题 
我们知道在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。
一般的Web应用划分为展现层、服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程

  ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。 
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。 或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。  线程安全问题都是由全局变量及静态变量引起的。  

  若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。
  1) 常量始终是线程安全的,因为只存在读操作。 
  2)每次调用方法前都新建一个实例是线程安全的,因为不会访问共享的资源。
  3)局部变量是线程安全的。因为每执行一个方法,都会在独立的空间创建局部变量,它不是共享的资源。局部变量包括方法的参数变量和方法内变量。
  有状态就是有数据存储功能。有状态对象(Stateful Bean),就是有实例变量的对象  ,可以保存数据,是非线程安全的。在不同方法调用间不保留任何状态。
  无状态就是一次操作,不能保存数据。无状态对象(Stateless Bean),就是没有实例变量的对象  .不能保存数据,是不变类,是线程安全的。
  有状态对象:
  无状态的Bean适合用不变模式,技术就是单例模式,这样可以共享实例,提高性能。有状态的Bean,多线程环境下不安全,那么适合用Prototype原型模式。Prototype: 每次对bean的请求都会创建一个新的bean实例。
  Struts2默认的实现是Prototype模式。也就是每个请求都新生成一个Action实例,所以不存在线程安全问题。需要注意的是,如果由Spring管理action的生命周期, scope要配成prototype作用域

二、线程安全案例

  SimpleDateFormat(下面简称sdf)类内部有一个Calendar对象引用,它用来储存和这个sdf相关的日期信息,例如sdf.parse(dateStr), sdf.format(date) 诸如此类的方法参数传入的日期相关String, Date等等, 都是交友Calendar引用来储存的.这样就会导致一个问题,如果你的sdf是个static的, 那么多个thread 之间就会共享这个sdf, 同时也是共享这个Calendar引用,非线程安全的。

  这个问题背后隐藏着一个更为重要的问题--无状态:无状态方法的好处之一,就是它在各种环境下,都可以安全的调用。衡量一个方法是否是有状态的,就看它是否改动了其它的东西,比如全局变量,比如实例的字段。format方法在运行过程中改动了SimpleDateFormat的calendar字段,所以,它是有状态的。

  这也同时提醒我们在开发和设计系统的时候注意下一下三点:
  1.自己写公用类的时候,要对多线程调用情况下的后果在注释里进行明确说明
  2.对线程环境下,对每一个共享的可变变量都要注意其线程安全性
  3.我们的类和方法在做设计的时候,要尽量设计成无状态的
  解决方法:

  1.使用synchronized关键字进行数据同步,或者使用ThreadLocal保证线程安全

  2.不适用JDK自带的时间格式化类,使用其他类库的

  •   使用Apache commons里的FastDateFormat,宣城是既快有线程安全的SimpleDateFormat,可惜他只能对日期进行format,不能对日期串进行解析
  •   使用Joda-Time类库来处理时间相关问题,该种对时间的处理方式比较完美,建议使用

  原文链接:https://www.cnblogs.com/redcool/p/6398760.html

转载于:https://www.cnblogs.com/mfrank/p/7891604.html

【转】Spring Bean单例与线程安全相关推荐

  1. 面试题剖析:单例设计模式线程安全问题

    本文作者:黄海燕,叩丁狼高级讲师.原创文章,转载请注明出处. 1. volatile 关键字 1.1 volatile 关键字作用: 在百度百科截取的描述如下: 叩丁狼教育.png 说明volatil ...

  2. spring作用域单例和global_面试必备 | 69道Spring面试题

    1.什么是spring? Spring 是个java企业级应用的开源开发框架.Spring主要用来开发Java应用,但是有些扩展是针对构建J2EE平台的web应用.Spring 框架目标是简化Java ...

  3. spring的单例回收

    首先.spring采用注册单例,符合注册单例特征. 被保存在ioc的缓存容器中,给每个实例起一个名字beanname.ID 在拿一个实例的时候只能从这个beanname中去取,也就意味着ioc容器持有 ...

  4. java双重检查锁单例真的线程安全吗?

     相信大多数同学在面试当中都遇到过手写单例模式的题目,那么如何写一个完美的单例是面试者需要深究的问题,因为一个严谨的单例模式说不定就直接决定了面试结果,今天我们就要来讲讲看似线程安全的双重检查锁单例模 ...

  5. 浅入 spring ioc 单例容器

    我们要干嘛? 1, 分析IOC 是怎么扫描路径 2, 对象是怎么被加载进容器的 3, 对象是怎么从容器里获取 4, 执行方法 案例代码, 网上一大顿分析xml的, 太low了, 我们直接从注解模式, ...

  6. java 线程安全的单例_线程安全的单例模式的几种实现

    单例模式是常见的设计模式之一:目的是节省内存,限制了实例的个数:有利于java GC回收机制. 单例模式的三个好处: 1.控制资源的使用,通过线程同步来控制资源的并发访问 2.控制实例的产生个数,来达 ...

  7. 静态工具类使用单例对象线程安全问题注意1

    1.记录问题 1.1工具类代码 public static SimpleDateFormat sdfTest = new SimpleDateFormat("yyyy年MM月dd日" ...

  8. 09_为什么说饿汉式单例是线程安全的?

    类加载的方式是按需加载,且只加载一次.因此,在上述单例类被加载时,就会实例化一个对象并交给自己的引用,供系统使用.换句话说,在线程访问单例对象之前就已经创建好了.再加上,由于一个类在整个生命周期中只会 ...

  9. Spring单例的线程安全性

    在Spring中单例bean是在beanfactory中用反射机制动态创建的,被缓存到ioc容器中相对于new了一个对象. 而这些创建的对象class,则是自己创建的. 所以是不是线程安全和Sprin ...

最新文章

  1. 有用的SAP System Administration T-CODE
  2. 用git rebase合并
  3. Codeforces Round #410 (Div. 2) D. Mike and distribution 思维+数学
  4. python查找序列元素的最大值和最小值_pthon基础知识(索引、切片、序列相加、乘法、检查元素是否是序列成员、计算序列长度、最大最小值)...
  5. oracle临时表空间暴涨,如何解决Oracle临时表空间过大
  6. 2台主机的docker互相通信的方法
  7. hpunix查看oracle监听,hp-ux 网络查看
  8. 基于 Flink 的实时数仓生产实践
  9. ghost还原固态硬盘_固态硬盘到底能不能使用Ghost软件?终于说明白了
  10. html怎么获取手机mac地址,js获取mac地址_Js获取客户端IP地址与MAC地址
  11. 如何将nideshop部署到本地
  12. BZOJ_4199_[Noi2015]品酒大会_后缀自动机
  13. 常用计量单位及其换算
  14. 学计算机选择师范类好吗,师范教育类专业和计算机类专业,两者相比,哪个更适合自考生报读...
  15. 第六节NoSQL+时序数据库+RabbitMQ安装
  16. 化工过程机械类毕业论文文献有哪些?
  17. iterm2分屏切换
  18. Hadoop系列(一)——HDFS总结
  19. matplotlib画的折线图
  20. 毒鸡汤词汇类的前端小程序源码模板

热门文章

  1. 重庆邮电大学计算机2019湖北分数线,重庆邮电大学2019各省高考录取分数线 投档分数线是多少...
  2. android绘制河流双曲线,项目三:河道测量试题库(13页)-原创力文档
  3. 文件上传linux服务器,Linux 文件上传Linux服务器
  4. 死磕java并发cas_死磕Java——CAS
  5. kafka删除队列_没想到 Kafka 还会这样问,学会这些带你轻松搞定大厂面试!
  6. 常见的邮件服务器有哪三种,常见邮件服务器的种类有哪些?分别用什么协议?,常见服务器类型...
  7. linux socket读写函数,Linux网络编程入门
  8. List(Map(String, Object))转为Fastjson JSONArray
  9. struts2结果类型
  10. 有了这款Python神器,新手也会调试代码!