⽬录
1.spring单例V.S设计模式的单例2.成员变量的解决⽅式3.Spring并发问题4.对实体bean在多线程中的处理5.spring⽆状态的⽀持6.spring有状态的⽀持7.ThreadLocal8.ThreadLocal使⽤9.ThreadLocalV.Ssynchronized10.Spring使⽤ThreadLocal解决线程安全问题1.spring单例 V.S 设计模式的单例
设计模式单例,在整个应⽤中只有⼀个实例spring单例,在⼀个IoC容器中只有⼀个实例
但spring中的单例也不影响应⽤并发访问。⼤多数时候客户端都在访问我们应⽤中的业务对象,为减少并发控制,不应该在业务对象中设置那些容易造成出错的成员变量。
2.成员变量的解决⽅式
⽅法的参数,局部变量(相当于new)threadlocal、设置bean scope=prototype
3.Spring并发问题
⼀般⽆状态的Bean才可在多线程环境下共享,Spring Bean默认为singleton作⽤域。那有状态bean呢?Spring对⼀些如
RequestContextHolder
TransactionSynchronizationManagerLocaleContextHolder
⾮线程安全状态Bean采⽤ThreadLocal,让它们也成为线程安全的状态。
如⽤有状态bean,也可使⽤prototype模式,每次在注⼊时,就重新创建⼀个bean,在多线程中互不影响。Eic-server所有的业务对象中的成员变量如:
Dao中的xxxDao
controller中的xxxService
都会被多个线程共享,那这些对象不会出现同步问题吗?⽐如造成DB插⼊,更新异常?
实体bean,从客户端传递到后台controller=》service=>Dao流程中,他们这些对象都是单例的,那这些单例对象在处理我们的传递到后台的实体bean不会出问题吗?(实体bean在多线程中的解决⽅案)
因为实体bean不是单例的,他们并没有交给Spring管理!每次我们都⼿动的New出来的,如BigObject bo = new BigObject(),所以即使是那些处理我们提交数据的业务处理类是被多线程共享,但他们处理的数据并不共享,数据是每个线程都有⾃⼰的⼀份,所以在数据⽅⾯不会出现线程安全问题。
4.对实体bean在多线程中的处理
对实体bean⼀般通过⽅法参数的的形式传递(参数是局部变量),所以多线程间不会有影响有的地⽅对有状态的bean直接使⽤prototype对使⽤bean的地⽅,可通过new创建
1. 在Dao中的xxxDao
2. controller中的xxxService
这些对象都是单例,那就不会出现线程同步问题。这些对象虽被多线程并发访问,可我们访问的是他们⾥⾯的⽅法,⽽这些类⾥⾯通常不会有成员变量。所以出问题的是系统⾥⾯的业务对象,务必注意这些业务对象⾥,千万不能有独⽴的成员变量,否则会出错。所以我们在应⽤中的业务对象如下:
controller中的成员变量List和paperService:service⾥的成员变量ibatisEntityDao:
虽然这个应⽤有成员变量,但不会出现线程安全问题:
controller⾥的成员变量private TestPaperService papersService 之所以会成为成员变量,我们的⽬的是注⼊,将其实例化进⽽访问⾥⾯的⽅法private static final int List = 0;是final的不会被改变
service⾥⾯的private IbatisEntityDao ibatisEntityDao;是框架本⾝的,线程同步问题已解决
5.spring⽆状态的⽀持
Spring框架对单例的⽀持是采⽤单例注册表。
6.spring有状态的⽀持
spring如何实现那些个有状态bean,如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder]的线程安全,即使⽤ThreadLocal实现的
Spring中ThreadLocal的使⽤7.ThreadLocal
当使⽤ThreadLocal维护变量(仅是变量,因为线程同步问题就是成员变量的互斥访问出问题)时,ThreadLocal为每个使⽤该变量的线程提供独⽴的变量副本,所以每个线程都可独⽴改变⾃⼰的副本,⽽不会影响其它线程所对应副本。
从线程⾓度看,就好像每⼀个线程都完全拥有该变量,这其实就将共享变相为⼈⼿⼀份。虽使⽤ThreadLocal带来更多内存开销,但这点开销还微不⾜道。因为保存在ThreadLocal中的对象,通常较⼩。
initialValue(),protected的⽅法,为⼦类重写⽽实现。该⽅法返回当前线程在该线程局部变量的初始值,是个延迟调⽤⽅法,在⼀个线程第1次调⽤get()或
set(Object)时才执⾏且仅执⾏1次。
8.ThreadLocal使⽤
要给线程初始化⼀个特殊值时,需要⾃⼰实现ThreadLocal的⼦类并重写该⽅法,通常使⽤⼀个内部匿名类对ThreadLocal进⾏⼦类化,EasyDBO中创建jdbc连接上下⽂就是这样做的:
简单实现版本:
9.ThreadLocal V.S synchronized
为保证多个线程对共享变量的安全访问,通常会使⽤synchronized保证同时只有⼀个线程对共享变量进⾏操作。但有些情况下,synchronized不能保证多线程对共享变量的正确读写。
例如类有个类变量,该类变量会被多个类⽅法读写,当多线程操作该类的实例对象时,若线程对类变量有读取、写⼊操作就会发⽣类变量读写错误,即便是在类⽅法前加上synchronized也⽆效,因为同⼀个线程在两次调⽤⽅法之间时锁是被释放的,这时其它线程可访问对象的类⽅法,读取或修改类变量。这种情况下可以将类变量放到ThreadLocal中,使变量在每个线程中都有独⽴拷贝,不会出现⼀个线程读取变量时⽽被另⼀个线程修改的现象。
多线程访问对于类变量和ThreadLocal变量的影响,QuerySvc分别设置:
类变量sql
ThreadLocal变量
使⽤时先创建QuerySvc的⼀个实例对象,然后产⽣多个线程,分别设置不同sql实例对象,再调⽤execute,读取sql的值,看是否是set⽅法中写⼊的值。这类似web应⽤中多个请求线程携带不同查询条件对⼀个servlet实例的访问,然后servlet调⽤业务对象,并传⼊不同查询条件,最后要保证每个请求得到的结果是对应的查询条件的结果。
使⽤QuerySvc的⼯作线程如下:
运⾏线程:
先创建⼀个QuerySvc实例对象,然后创建若⼲线程来调⽤QuerySvc的set和execute⽅法,每个线程传⼊的sql都不⼀样,sql变量中值不能保证在execute中值和set设置的值⼀样,在web应⽤中就表现为⼀个⽤户查询的结果不是⾃⼰的查询条件返回的结果,⽽是另⼀个⽤户查询条件的结果。⽽ThreadLocal中的值总是和set中设置的值⼀样,这样通过使⽤ThreadLocal获得了线程安全性。⼩结:
若⼀个对象要被多个线程访问,⽽该对象存在类变量被不同类⽅法读写,为获得线程安全,可以⽤ThreadLocal替代类变量。ThreadLocal和线程同步机制相⽐有什么优势呢?
ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。
同步机制中,通过对象的锁机制保证同⼀时间只有⼀个线程访问变量。这时该变量是多个线程共享的,使⽤同步机制要分析:
什么时候对变量进⾏读写什么时候需要锁定某个对象
什么时候释放对象锁等繁杂的问题
⽽ThreadLocal为每个线程提供⼀个独⽴变量副本,隔离多线程对数据的访问冲突。ThreadLocal采⽤“以空间换时间”。
10.Spring使⽤ThreadLocal解决线程安全问题
⼀般只有⽆状态Bean才能在多线程下共享,在Spring中,绝⼤部分Bean都可以声明为singleton作⽤域。因为Spring对⼀些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中⾮线程安全状态采⽤ThreadLocal进⾏处理,让它们也成为线程安全的状态,有状态Bean就能在多线程中共享了。
⼀般Web应⽤划分为展现层、服务层和持久层三个层次,从接收请求到返回响应所经过的所有程序调⽤都同属于⼀个线程。这就能根据需要,将⼀些⾮线程安全的变量以ThreadLocal存放,在同⼀次请求响应的调⽤线程中,所有关联的对象引⽤到的都是同⼀个变量。
ThreadLocal是解决线程安全问题⼀个很好的思路,它通过为每个线程提供⼀个独⽴的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal⽐直接使⽤synchronized同步机制解决线程安全问题更简单,更⽅便,且结果程序拥有更⾼的并发性。
到此这篇关于Java中Spring的单例模式使⽤的⽂章就介绍到这了,更多相关Spring的单例模式使⽤内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!
因篇幅问题不能全部显示,请点此查看更多更全内容