使用Spring时经常会问,我们定义的Bean应该是Singleton还是Prototype?多个客户端同时调用Dao层,需要考虑线程安全吗?通过阅读官方文档和Spring的源代码,这类问题的答案是:自定义的Stateless Bean是不需要考虑线程安全问题的,可以在配置时设置为Singleton,减少new操作,提高程序效率;自定义的Stateful Bean是需要考虑线程安全问题的,Spring没有提供任何安全机制,只能由开发人员自己处理,比如使用ThreadLocal的方式或在配置时设置为Prototype。对于Dao层,Spring框架做了特殊处理。DataSource声明为Singleton模式,但其中的Connections是由支持线程安全的集合保存。与此同时,EntityManagerFactory是线程安全的,EntityManager不是,所以在每个Dao函数中都会首先执行:EntityManager entityManager = entityManagerFactory.createEntityManager()。如果追溯到最基本的JdbcTemplate用法,其官网的实例代码是:

 
  1. public class JdbcCorporateEventDao implements CorporateEventDao {
  2.  
  3. private JdbcTemplate jdbcTemplate;
  4.  
  5. public void setDataSource(DataSource dataSource) {
  6. this.jdbcTemplate = new JdbcTemplate(dataSource);
  7. }
  8. }
 

这表明每一个客户端调用都会产生一个新的JdbcTemplate对象,所以JdbcCorporateEventDao也就不会出现线程安全问题了。下面的代码是一个自定义的有状态Bean。程序运行结果表明,两个线程都在改变它的状态(成员变量),并且这种影响是不确定的。

 
  1. package com.mmh.main;
  2.  
  3. import java.util.concurrent.ExecutorService;
  4. import java.util.concurrent.Executors;
  5.  
  6. import org.springframework.context.ApplicationContext;
  7. import org.springframework.context.support.ClassPathXmlApplicationContext;
  8.  
  9. import com.mmh.printer.PrintHelper;
  10.  
  11. public class Application {
  12.  
  13. public static void main(String[] args) {
  14. ApplicationContext context = new ClassPathXmlApplicationContext(
  15. "appContext.xml");
  16.  
  17. ExecutorService executors = Executors.newCachedThreadPool();
  18.  
  19. executors.execute(new PrintThread(context));
  20. executors.execute(new PrintThread(context));
  21. }
  22. }
  23.  
  24. class PrintThread implements Runnable {
  25.  
  26. private ApplicationContext context;
  27.  
  28. public PrintThread(ApplicationContext context) {
  29. this.context = context;
  30. }
  31.  
  32. public void run() {
  33. PrintHelper printHelper = context.getBean(PrintHelper.class);
  34.  
  35. try {
  36. Thread.sleep(10);
  37. } catch (InterruptedException e) {
  38. e.printStackTrace();
  39. }
  40.  
  41. for (int i = 0; i < 2; i++) {
  42. printHelper.print();
  43. printHelper.increamentYears();
  44. }
  45. }
  46. }
  47. // 输出结果:
  48. /*
  49. * Thread[pool-1-thread-1,5,main]: yzp---28
  50. * Thread[pool-1-thread-2,5,main]: yzp---28
  51. * Thread[pool-1-thread-2,5,main]: yzp---30
  52. * Thread[pool-1-thread-1,5,main]: yzp---29
  53. */
 

  把自定义Bean的scope设定为:prototype,程序运行结果表明,它的状态没有被两个线程相互干扰。

 
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans.xsd">
  6.  
  7. <bean id="printHelper" class="com.mmh.printer.PrintHelper" scope="prototype">
  8. <constructor-arg index="0" value="yzp"/>
  9. <constructor-arg index="1" value="28"/>
  10. </bean>
  11.  
  12. </beans>
 
 
  1. // 输出结果:
  2. /*
  3. * Thread[pool-1-thread-1,5,main]: yzp---28
  4. * Thread[pool-1-thread-1,5,main]: yzp---29
  5. * Thread[pool-1-thread-2,5,main]: yzp---28
  6. * Thread[pool-1-thread-2,5,main]: yzp---29
  7. */
 

  在debug时跟踪代码可以看到,IoC容器初始化时会创建一个注册类DefaultSingletonBeanRegistry,在这个类里有这样一行代码:private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64),其中的singletonObjects就是保存SingletonBean的集合,它的类型是ConcurrentHashMap,该集合本身支持线程安全。下面的代码是Spring的源代码,getBean()获取SingletonBean的核心内容。

 
  1. protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  2. Object singletonObject = this.singletonObjects.get(beanName);
  3. if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
  4. synchronized (this.singletonObjects) {
  5. singletonObject = this.earlySingletonObjects.get(beanName);
  6. if (singletonObject == null && allowEarlyReference) {
  7. ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
  8. if (singletonFactory != null) {
  9. singletonObject = singletonFactory.getObject();
  10. this.earlySingletonObjects.put(beanName, singletonObject);
  11. this.singletonFactories.remove(beanName);
  12. }
  13. }
  14. }
  15. }
  16. return (singletonObject != NULL_OBJECT ? singletonObject : null);
  17. }
 

如果singletonObjects中有需要的Bean,就直接取出来返回,如果没有,那么在同步块中使用ObjectFactory创建一个新的Bean。由代码可以看到,无论是直接获取还是新创建,操作过程都是线程安全的。

Threads in Spring的更多相关文章

  1. java 和 spring 的异步

    spring 的 async 注解 https://www.baeldung.com/spring-async@Async will make it execute in a separate thr ...

  2. SpringBoot开发案例之整合Dubbo分布式服务

    前言 在 SpringBoot 很火热的时候,阿里巴巴的分布式框架 Dubbo 不知是处于什么考虑,在停更N年之后终于进行维护了.在之前的微服务中,使用的是当当维护的版本 Dubbox,整合方式也是使 ...

  3. multi-threads JavaEE 容器

    Thread -- Request What is recommended way for spawning threads from a servlet in Tomcat [duplicate] ...

  4. Session挂起

    异常信息: toString() unavailable - no suspended threads 使用Spring管理 ,在使用hibernate时使用如下语句Session session = ...

  5. Spring webapp - shutting down threads on Application stop

    显示使用线程池Executors,必须执行 pool.shutdown() 否则会存在线程池泄露: http://stackoverflow.com/questions/22650569/spring ...

  6. Spring cache简单使用guava cache

    Spring cache简单使用 前言 spring有一套和各种缓存的集成方式.类似于sl4j,你可以选择log框架实现,也一样可以实现缓存实现,比如ehcache,guava cache. [TOC ...

  7. 第一个Spring Boot Web程序

    需要的环境和工具: 1.Eclipse2.Java环境(JDK 1.7或以上版本)3.Maven 3.0+(Eclipse已经内置了) 写个Hello Spring: 1.新建一个Maven项目,项目 ...

  8. Spring boot配置文件 application.properties

    http://www.tuicool.com/articles/veUjQba 本文记录Spring Boot application.propertis配置文件的相关通用属性 # ========= ...

  9. Spring MVC 学习总结(三)——请求处理方法Action详解

    Spring MVC中每个控制器中可以定义多个请求处理方法,我们把这种请求处理方法简称为Action,每个请求处理方法可以有多个不同的参数,以及一个多种类型的返回结果. 一.Action参数类型 如果 ...

随机推荐

  1. 机器学习中的范数规则化之(一)L0、L1与L2范数(转)

    http://blog.csdn.net/zouxy09/article/details/24971995 机器学习中的范数规则化之(一)L0.L1与L2范数 zouxy09@qq.com http: ...

  2. java中的反射机制,以及如何通过反射获取一个类的构造方法 ,成员变量,方法,详细。。

    首先先说一下类的加载,流程.只有明确了类这个对象的存在才可以更好的理解反射的原因,以及反射的机制. 一.  类的加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三 ...

  3. Binary Tree Level Order Traversal II

    /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode ...

  4. maven打包异常

    maven打包异常,如图: 问题原因:服务器密码错了.

  5. ios app的真机调试与发布配置

    1.打开应用程序—>[钥匙串访问]—>[证书助理]—>[从证书办法机构请求证书]     2.在[用户电子邮件地址]填入apple账户用的邮箱,选择[存储到磁盘],点击[继续],会在 ...

  6. HDU 4417:Super Mario(主席树)

    http://acm.hdu.edu.cn/showproblem.php?pid=4417 题意是:给出n个数和q个询问,每个询问有一个l,r,h,问在[l,r]这个区间里面有多少个数是小于等于h的 ...

  7. POJ 3237:Tree(树链剖分)

    http://poj.org/problem?id=3237 题意:树链剖分.操作有三种:改变一条边的边权,将 a 到 b 的每条边的边权都翻转(即 w[i] = -w[i]),询问 a 到 b 的最 ...

  8. ACM第四站————最小生成树(克鲁斯卡尔算法)

    都是生成最小生成树,库鲁斯卡尔算法与普里姆算法的不同之处在于——库鲁斯卡尔算法的思想是以边为主,找权值最小的边生成最小生成树. 主要在于构建边集数组,然后不断寻找最小的边. 同样的题目:最小生成树 题 ...

  9. LA 4126 Password Suspects

    问题描述:给定m个模式串,计数包含所有模式串且长度为n的字符串的数目. 数据范围:模式串长度不超过10,m <= 10, n <= 25,此外保证答案不超过1015. 分析:既然要计数给定 ...

  10. poj2395 Out of Hay

    题意就是给你一张无向连通图,试问对于图上所有点对(u,v)从u到v的所有路径中边权最大值的最小值的最大值. 定义f(u,v)表示从u到v所有路径中边权最大值的最小值,对所有点对取其最大. 实际上就是求 ...