Spring通过单实例化Bean简化多线程问题

   由于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。 

启动独立线程调用事务方法  

 
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...


   在①处,在主线程(main)执行的UserService#logon()方法的事务启动,在②处,其对应的事务提交。而在子线程(Thread-2)执行的ScoreService#addScore()方法的事务在③处启动,在④处对应的事务提交。 
   所以,我们可以得出这样的结论:在相同线程中进行相互嵌套调用的事务方法工作于相同的事务中。如果这些相互嵌套调用的方法工作在不同的线程中,则不同线程下的事务方法工作在独立的事务中。 

  注:以上内容摘自《Spring 3.x企业应用开发实战》
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值