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项目之----企业网站24之网站前台列表页面包屑导航的显示
我们做面包屑导航的原理其实也是很简单的,我们的思路是: 首先找到该分类的id ,我们可以通过大 I来进行获取得到: 然后通过 大 D 方法让数据进入model层里面进行循环迭代查询, 当然,测试时候发 ...
- PHP自动生成后台导航网址的最佳方法
'http://www.jbxue.com'=> '脚本学堂首页', </script>
- Mongodb 笔记06 副本集的组成、从应用程序连接副本集、管理
副本集的组成 1. 同步:MongoDB的复制功能是使用操作日志oplog实现的,操作日志包含了主节点的每一次写操作.oplog是主节点的local数据库中的一个固定集合.备份节点通过查询整个集合就可 ...
- s3c2440 lcd 显示图片裸机程序
因为前面的裸机程序非常的简单,就不写博了. 程序的流程: 1,初始化C SP 2,关看门狗 3,初始化SDRAM 4,读出 NAND FLASH 中的 包含图片的程式放到SDRAM里面 5,跳转到SD ...
- oracle SGA详解
SGA(System Global Area)系统全局区.这是一个非常庞大的内存区间,也是为什么开启oracle之后占用了很大内存的原因. SGA分为不同的池,我们可以通过视图v$sgastat查看, ...
- mysql delete数据 空间占用不减少的解决办法
今天空间商告诉我数据库空间满了,检查了一下,发现网站用户行为记录数据表竟然占了20多MB.积累了半年了,该删除释放一下空间了.果断delete之后发现数据库空间竟然没少,虽然数据记录数是零. 原来这是 ...
- plsql日期乱码
乱码状况如截图: 控制面板\所有控制面板项\系统\高级系统设置\环境变量, 设置系统变量,变量名:NLS_LANG,变量值:Simplified Chinese_China.AL32UTF8改为SIM ...
- c#之委托所有方法
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Cons ...
- c#游戏 剪刀石头
电脑using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace sh ...
- 【20160924】GOCVHelper MFC增强算法(1)
//递归读取目录下全部文件(flag为r的时候递归) void getFiles(string path, vector<string>& files,string ...