Spring通过单实例化Bean简化多线程问题
来源:互联网 发布:质数优化算法 编辑:程序博客网 时间:2024/05/15 00:35
由于Spring的事务管理器是通过线程相关的ThreadLocal来保存数据访问基础设施(也即Connection实例),再结合IoC和AOP实现高级声明式事务的功能,所以Spring的事务天然地和线程有着千丝万缕的联系。
我们知道Web容器本身就是多线程的,Web容器为一个HTTP请求创建一个独立的线程(实际上大多数Web容器采用共享线程池),所以由此请求所牵涉到 的Spring容器中的Bean也是运行于多线程的环境下。在绝大多数情况下,Spring的Bean都是单实例的(singleton),单实例 Bean的最大好处是线程无关性,不存在多线程并发访问的问题,也就是线程安全的。
一个类能够以单实例的方式运行的前提是“无状态”:即一个类不能拥有状态化的成员变量。我们知道,在传统的编程中,DAO必须持有一个 Connection,而Connection即是状态化的对象。所以传统的DAO不能做成单实例的,每次要用时都必须创建一个新的实例。传统的 Service由于内部包含了若干个有状态的DAO成员变量,所以其本身也是有状态的。
但是在Spring中,DAO和Service都以单实例的方式存在。Spring是通过ThreadLocal将有状态的变量(如Connection 等)本地线程化,达到另一个层面上的“线程无关”,从而实现线程安全。Spring不遗余力地将有状态的对象无状态化,就是要达到单实例化Bean的目 的。
由于Spring已经通过ThreadLocal的设施将Bean无状态化,所以Spring中单实例Bean对线程安全问题拥有了一种天生的免疫能力。 不但单实例的Service可以成功运行于多线程环境中,Service本身还可以自由地启动独立线程以执行其他的Service。
启动独立线程调用事务方法
将日志级别设置为DEBUG,执行UserService#logon()方法,观察以下输出日志:
在①处,在主线程(main)执行的UserService#logon()方法的事务启动,在②处,其对应的事务提交。而在子线程(Thread-2)执行的ScoreService#addScore()方法的事务在③处启动,在④处对应的事务提交。
所以,我们可以得出这样的结论:在相同线程中进行相互嵌套调用的事务方法工作于相同的事务中。如果这些相互嵌套调用的方法工作在不同的线程中,则不同线程下的事务方法工作在独立的事务中。
注:以上内容摘自《Spring 3.x企业应用开发实战》
我们知道Web容器本身就是多线程的,Web容器为一个HTTP请求创建一个独立的线程(实际上大多数Web容器采用共享线程池),所以由此请求所牵涉到 的Spring容器中的Bean也是运行于多线程的环境下。在绝大多数情况下,Spring的Bean都是单实例的(singleton),单实例 Bean的最大好处是线程无关性,不存在多线程并发访问的问题,也就是线程安全的。
一个类能够以单实例的方式运行的前提是“无状态”:即一个类不能拥有状态化的成员变量。我们知道,在传统的编程中,DAO必须持有一个 Connection,而Connection即是状态化的对象。所以传统的DAO不能做成单实例的,每次要用时都必须创建一个新的实例。传统的 Service由于内部包含了若干个有状态的DAO成员变量,所以其本身也是有状态的。
但是在Spring中,DAO和Service都以单实例的方式存在。Spring是通过ThreadLocal将有状态的变量(如Connection 等)本地线程化,达到另一个层面上的“线程无关”,从而实现线程安全。Spring不遗余力地将有状态的对象无状态化,就是要达到单实例化Bean的目 的。
由于Spring已经通过ThreadLocal的设施将Bean无状态化,所以Spring中单实例Bean对线程安全问题拥有了一种天生的免疫能力。 不但单实例的Service可以成功运行于多线程环境中,Service本身还可以自由地启动独立线程以执行其他的Service。
启动独立线程调用事务方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package
com.baobaotao.multithread;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.jdbc.core.JdbcTemplate;
import
org.springframework.context.ApplicationContext;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
import
org.springframework.stereotype.Service;
import
org.apache.commons.dbcp.BasicDataSource;
@Service
(
"userService"
)
public
class
UserService
extends
BaseService {
@Autowired
private
JdbcTemplate jdbcTemplate;
@Autowired
private
ScoreService scoreService;
public
void
logon(String userName) {
System.out.println(
"before userService.updateLastLogonTime method..."
);
updateLastLogonTime(userName);
System.out.println(
"after userService.updateLastLogonTime method..."
);
//scoreService.addScore(userName, 20);//①在同一线程中调用scoreService#addScore()
//②在一个新线程中执行scoreService#addScore()
Thread myThread =
new
MyThread(
this
.scoreService, userName,
20
);
//使用一个新线程运行
myThread.start();
}
public
void
updateLastLogonTime(String userName) {
String sql =
"UPDATE t_user u SET u.last_logon_time = ? WHERE user_name =?"
;
jdbcTemplate.update(sql, System.currentTimeMillis(), userName);
}
//③负责执行scoreService#addScore()的线程类
private
class
MyThread
extends
Thread {
private
ScoreService scoreService;
private
String userName;
private
int
toAdd;
private
MyThread(ScoreService scoreService, String userName,
int
toAdd) {
this
.scoreService = scoreService;
this
.userName = userName;
this
.toAdd = toAdd;
}
public
void
run() {
try
{
Thread.sleep(
2000
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
System.out.println(
"before scoreService.addScor method..."
);
scoreService.addScore(userName, toAdd);
System.out.println(
"after scoreService.addScor method..."
);
}
}
}
将日志级别设置为DEBUG,执行UserService#logon()方法,观察以下输出日志:
引用
before userService.logon method...
//①创建一个事务
Creating new transaction with name [com.baobaotao.multithread.UserService.logon]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
Acquired Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost, MySQL-AB JDBC Driver] for JDBC transaction
…
SQL update affected 1 rows
after userService.updateLastLogonTime method...
Initiating transaction commit
//②提交①处开启的事务
Committing JDBC transaction on Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost, MySQL-AB JDBC Driver]
Releasing JDBC Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost, MySQL-AB JDBC Driver] after transaction
…
Returning JDBC Connection to DataSource
before scoreService.addScor method...
//③创建一个事务
Creating new transaction with name [com.baobaotao.multithread.ScoreService.addScore]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
Acquired Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost, MySQL-AB JDBC Driver] for JDBC transaction
…
SQL update affected 0 rows
Initiating transaction commit
//④提交③处开启的事务
Committing JDBC transaction on Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost, MySQL-AB JDBC Driver]
Releasing JDBC Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost, MySQL-AB JDBC Driver] after transaction
Returning JDBC Connection to DataSource
after scoreService.addScor method...
//①创建一个事务
Creating new transaction with name [com.baobaotao.multithread.UserService.logon]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
Acquired Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost, MySQL-AB JDBC Driver] for JDBC transaction
…
SQL update affected 1 rows
after userService.updateLastLogonTime method...
Initiating transaction commit
//②提交①处开启的事务
Committing JDBC transaction on Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost, MySQL-AB JDBC Driver]
Releasing JDBC Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost, MySQL-AB JDBC Driver] after transaction
…
Returning JDBC Connection to DataSource
before scoreService.addScor method...
//③创建一个事务
Creating new transaction with name [com.baobaotao.multithread.ScoreService.addScore]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
Acquired Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost, MySQL-AB JDBC Driver] for JDBC transaction
…
SQL update affected 0 rows
Initiating transaction commit
//④提交③处开启的事务
Committing JDBC transaction on Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost, MySQL-AB JDBC Driver]
Releasing JDBC Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost, MySQL-AB JDBC Driver] after transaction
Returning JDBC Connection to DataSource
after scoreService.addScor method...
在①处,在主线程(main)执行的UserService#logon()方法的事务启动,在②处,其对应的事务提交。而在子线程(Thread-2)执行的ScoreService#addScore()方法的事务在③处启动,在④处对应的事务提交。
所以,我们可以得出这样的结论:在相同线程中进行相互嵌套调用的事务方法工作于相同的事务中。如果这些相互嵌套调用的方法工作在不同的线程中,则不同线程下的事务方法工作在独立的事务中。
注:以上内容摘自《Spring 3.x企业应用开发实战》
0 0
- Spring通过单实例化Bean简化多线程问题
- spring bean 实例化问题
- Spring 实例化bean
- spring实例化bean
- Spring实例化bean
- Spring通过FactoryBean创建bean的实例
- Sping中使用单实例化简化多线程的相关实现 事务 模板 回调
- Spring Bean配置默认为单实例 pring Bean生命周期
- Spring Bean配置默认为单实例 pring Bean生命周期
- Spring Bean配置默认为单实例 pring Bean生命周期
- Servlet单实例多线程问题
- servlet单实例多线程问题
- Servlet单实例多线程问题
- spring bean的scope属性解决struts action单实例问题
- Bean实例化问题
- 解决spring国际化不能实例化bean的问题
- Spring 多线程下注入bean问题
- Spring 多线程下注入bean问题
- FFmpeg编程学习笔记二:音频重采样
- hdu 1250 Hat's Fibonacci
- 位段 bitfield
- HDU-1231-最大连续子序列【dp】
- 使用ffmpeg实现转码样例(代码实现)
- Spring通过单实例化Bean简化多线程问题
- 连接COM与.NET的桥梁(二)——COM服务器的P/Invoke方式
- mysql中like模糊查询优化
- 位段 bitfield
- .Net MVC 文件上传和下载
- android安全:forceStopPackage对android的Alarm的影响
- oracle删除表空间文件后表空间无法删除
- Synchronization 同步
- mongodb mapreduce 分片