Threads in Spring
使用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用法,其官网的实例代码是:
public class JdbcCorporateEventDao implements CorporateEventDao {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
}
这表明每一个客户端调用都会产生一个新的JdbcTemplate对象,所以JdbcCorporateEventDao也就不会出现线程安全问题了。下面的代码是一个自定义的有状态Bean。程序运行结果表明,两个线程都在改变它的状态(成员变量),并且这种影响是不确定的。
package com.mmh.main; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import com.mmh.printer.PrintHelper; public class Application { public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"appContext.xml"); ExecutorService executors = Executors.newCachedThreadPool(); executors.execute(new PrintThread(context));
executors.execute(new PrintThread(context));
}
} class PrintThread implements Runnable { private ApplicationContext context; public PrintThread(ApplicationContext context) {
this.context = context;
} public void run() {
PrintHelper printHelper = context.getBean(PrintHelper.class); try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
} for (int i = 0; i < 2; i++) {
printHelper.print();
printHelper.increamentYears();
}
}
}
// 输出结果:
/*
* Thread[pool-1-thread-1,5,main]: yzp---28
* Thread[pool-1-thread-2,5,main]: yzp---28
* Thread[pool-1-thread-2,5,main]: yzp---30
* Thread[pool-1-thread-1,5,main]: yzp---29
*/
把自定义Bean的scope设定为:prototype,程序运行结果表明,它的状态没有被两个线程相互干扰。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="printHelper" class="com.mmh.printer.PrintHelper" scope="prototype">
<constructor-arg index="0" value="yzp"/>
<constructor-arg index="1" value="28"/>
</bean> </beans>
// 输出结果:
/*
* Thread[pool-1-thread-1,5,main]: yzp---28
* Thread[pool-1-thread-1,5,main]: yzp---29
* Thread[pool-1-thread-2,5,main]: yzp---28
* Thread[pool-1-thread-2,5,main]: yzp---29
*/
在debug时跟踪代码可以看到,IoC容器初始化时会创建一个注册类DefaultSingletonBeanRegistry,在这个类里有这样一行代码:private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64),其中的singletonObjects就是保存SingletonBean的集合,它的类型是ConcurrentHashMap,该集合本身支持线程安全。下面的代码是Spring的源代码,getBean()获取SingletonBean的核心内容。
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
如果singletonObjects中有需要的Bean,就直接取出来返回,如果没有,那么在同步块中使用ObjectFactory创建一个新的Bean。由代码可以看到,无论是直接获取还是新创建,操作过程都是线程安全的。
Threads in Spring的更多相关文章
- java 和 spring 的异步
spring 的 async 注解 https://www.baeldung.com/spring-async@Async will make it execute in a separate thr ...
- SpringBoot开发案例之整合Dubbo分布式服务
前言 在 SpringBoot 很火热的时候,阿里巴巴的分布式框架 Dubbo 不知是处于什么考虑,在停更N年之后终于进行维护了.在之前的微服务中,使用的是当当维护的版本 Dubbox,整合方式也是使 ...
- multi-threads JavaEE 容器
Thread -- Request What is recommended way for spawning threads from a servlet in Tomcat [duplicate] ...
- Session挂起
异常信息: toString() unavailable - no suspended threads 使用Spring管理 ,在使用hibernate时使用如下语句Session session = ...
- Spring webapp - shutting down threads on Application stop
显示使用线程池Executors,必须执行 pool.shutdown() 否则会存在线程池泄露: http://stackoverflow.com/questions/22650569/spring ...
- Spring cache简单使用guava cache
Spring cache简单使用 前言 spring有一套和各种缓存的集成方式.类似于sl4j,你可以选择log框架实现,也一样可以实现缓存实现,比如ehcache,guava cache. [TOC ...
- 第一个Spring Boot Web程序
需要的环境和工具: 1.Eclipse2.Java环境(JDK 1.7或以上版本)3.Maven 3.0+(Eclipse已经内置了) 写个Hello Spring: 1.新建一个Maven项目,项目 ...
- Spring boot配置文件 application.properties
http://www.tuicool.com/articles/veUjQba 本文记录Spring Boot application.propertis配置文件的相关通用属性 # ========= ...
- Spring MVC 学习总结(三)——请求处理方法Action详解
Spring MVC中每个控制器中可以定义多个请求处理方法,我们把这种请求处理方法简称为Action,每个请求处理方法可以有多个不同的参数,以及一个多种类型的返回结果. 一.Action参数类型 如果 ...
随机推荐
- 夺命雷公狗ThinkPHP项目之----企业网站4之数据库连接
我们众所周知,我们在开发的时候网站是需要连接上我们的数据库的,毕竟数据库是网站的最核心之一嘛,废话不多说直接开干.... 我们先找到:config.php文件对她进行修改,因为我们网站前后台都用到数据 ...
- 1. 星际争霸之php面向对象(一)
题记==============================================================================本php设计模式专辑来源于博客(jymo ...
- 绑定repeater时三目运算加特殊结果处理
<%#((Convert.ToDouble().ToString() != ).ToString(%
- OpenCV cv::Mat类
using namespace cv; 1.Mat的声明: Mat m=Mat(rows, cols, type); Mat m=Mat(Size(width,height), type); type ...
- Oracle DB SQL 性能分析器
• 确定使用SQL 性能分析器的优点 • 描述SQL 性能分析器工作流阶段 • 使用SQL 性能分析器确定数据库更改所带来的性能改进 SQL 性能分析器:概览 • 11g 的新增功能 • 目标用户:D ...
- python实现指定目录下批量文件的单词计数:串行版本
直接上代码. 练习目标: 1. 使用 Python 面向对象的方法封装逻辑和表达 : 2. 使用异常处理和日志API : 3. 使用文件目录读写API : 4. 使用 list, map, t ...
- linux设备驱动归纳总结(八):3.设备管理的分层与面向对象思想【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-110738.html linux设备驱动归纳总结(八):3.设备管理的分层与面向对象思想 xxxxxx ...
- 测试驱动开发神器框架Mockito
作为菜鸟的我,以前没接触过Mock类型的框架,比如说要测试action层,我总是从action层调用service再调用dao访问数据库,这种方式从原则上来说是无疑是非常正确的,在没用mock框架之前 ...
- (个人)读取A.CSV修改它的某列,写入B.CSV
#!/usr/bin/perl -wuse strict;use warnings;use Tie::File; open(IN_FILE,"<E:/Hzj_works/test1.c ...
- Hibernate,JPA注解@Version
Hibernate实现悲观锁和乐观锁. 1,悲观锁 用例代码如下: 数据库DDL语句: hibernate.cfg.xml java类 以上代码(除下面的main之外)同乐观锁. main packa ...