Spring如何使用ThreadLocal解决线程安全问题

乐山ing 发布于 2016/08/30 11:04
阅读 2K+
收藏 1

看“Spring企业应用开发实战”这本书,里面写到


Spring通过ThreadLocal将现有状态的变量(如Connection等)本地线程化,达到另一个层面是的“线程无关”,从而实现线程安全。

现在有这样一个例子,如一个控制器UserController是单例的:

@Controller
public class UserController {
    @Autowired
    private UserDao userDao;

     ....

}

UserController是单例,那么对所有的HTTP请求来说都是访问同一个UserController实例,其中UserDao也是单例,也只有一个UserDao实例。同样的,userDao里面的connection也只有一个,那么connection就无法实现线程无关。没有看出来哪里使用了ThreadLocal?也没有看出来是如何解决线程无线这个问题?

比如

@Repository
UserDao{
      private Connection connection;
       .....
}

这段代码是线程安全的吗?有没有使用到ThreadLocal?
还是说一定要使用如下形式,才能保证线程安全

@Repository

UserDao{

      private ThreadLocal<Connection> connHolder;     

    .....

}

加载中
1
JoeyXie
JoeyXie
可以参考一下这篇文章
一般DAO中自动装配的都是EntityManager或者SessionFactory对象,且是一个代理对象,它内部会维护一个ThreadLocal对象,把当前线程跟Connection的映射保存起来,这样每个请求线程就能拿到自己对应的connection去操作数据库了,不会有线程安全问题。而@Controller、@Service和@Dao默认都是单实例的,所以没用到ThreadLocal,假如它们的类成员变量不做线程安全控制是有可能会发生线程安全问题的,实际项目中虽然全部请求都用同一个Controller、Service和Dao去处理,但是Dao中装配的SessionFactory是线程安全的,所以也就可以保证线程安全问题了,如果你自己在Dao中维护一个connection对象,肯定会存在线程安全问题
JoeyXie
JoeyXie
@乐山ing 这个我没有做过实验对比,不好下结论啊,不过struts现在都不怎么用了,这是一个事实20
乐山ing
乐山ing
回复 @JoeyXie : struts2似乎用的是prototype,每个请求生成一个新的Action,那么是否会比较慢呢?或者说比较消耗资源
JoeyXie
JoeyXie
@乐山ing 我个人感觉是的,因为假如对每个请求都创建一个controller、service和dao对象,那这样会在jvm堆中分配很多内存,加快了垃圾回收的触发,而且创建对象也有开销,所以事先把该准备的准备好,应该能提升不少性能吧...
乐山ing
乐山ing
好像Spring特别偏爱单例模式,是因为性能的原因吗?
1
ksfzhaohui
ksfzhaohui
UserController,userDao 本身都是没有状态的,何来需要保证需要线程安全
乐山ing
乐山ing
回复 @ksfzhaohui : 嗯,我猜也可能是这个原因。JdbcTemplate内部的实现确实使用了ThreadLocal。所以即使不同线程访问唯一的单例实例,也可以为不同的线程获得不同的Connection
ksfzhaohui
ksfzhaohui
回复 @乐山ing : 使用spring没有直接在dao里面引用connection的,一般都是用JdbcTemplate的,在JdbcTemplate里面会获取connection,此时会使用到ThreadLocal
乐山ing
乐山ing
是不是说 @Repository UserDao{ private Connection connection; ..... } 这样的写法就是错误的,因为引入了状态
0
skhuhu
skhuhu
单例是对于每个线程而言的···每个http请求都是单独的线程···
skhuhu
skhuhu
自己做个试验···在controller 里面 定义个public int a = 0; 你多线程调用····看a 的值变化 ···
乐山ing
乐山ing
如果是单例,对于多个线程来说只有一个UserController啊。也就是说多个Http请求请求的是同一个UserController实例
0
首席撸出血
首席撸出血
如果是单例,对于多个线程来说只有一个UserController啊。
也就是说多个Http请求请求的是同一个UserController实例


上面两句是你说的。

你得研究一下spring的Controller这个注解的使用

其实每个http请求的调用都是独立的!不是同一个实例!


其实J2EE的规范里,Servlet是原子性的,所以servlet里面的方法调用是线程安全的,Spring里面Servlet是DispatchServlet入口,然后匹配到对应的Controller类,用IOC调用对应的代码,所以Controller并非同一个实例。


首席撸出血
首席撸出血
回复 @乐山ing : 是的,我错了,这里有个博客,可以一起学习http://2277259257.iteye.com/blog/2300298
乐山ing
乐山ing
你做个实验就会发现只有一个UserController
0
大大大白熊
大大大白熊
每个线程有个数据副本,如果在controller,或者是service层中,没有定义类变量的情况下,都是线程安全的,因为每个线程只访问自己数据副本中的值,如果是定义了类变量,类变量的值会被不同的线程给覆盖。所以会导致线程不安全。treadLocal实际上是一个MAP,key就是当前的线程名,所以是针对每个线程的,使用ThreadLocal的get方法的时候,取得是当前线程中存放的值
大大大白熊
大大大白熊
回复 @乐山ing : controller和service在spring默认都是单例的,并不没有用ThreadLocal,都不是线程安全的,所以在spring中组件,要么指定scope,要么就只是用局部变量,别用类变量
乐山ing
乐山ing
可是没看见在从“请求”到“Controler、Service、Dao”这些环节,哪里使用到了ThreadLocal
0
晚饭吃饱没
晚饭吃饱没
就是把需要多个进程共享的变量放到ThreadLocal里面管理就能做到线程安全了
返回顶部
顶部