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

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

java多线程保证事务一致性_单实例


   将日志级别设置为DEBUG,执行UserService#logon()方法,观察以下输出日志: 

1. package
2.   
3. import
4. import
5. import
6. import
7. import
8. import
9.   
10. @Service("userService")  
11. public class UserService extends
12. @Autowired
13. private
14.   
15. @Autowired
16. private
17.   
18. public void
19. "before userService.updateLastLogonTime method...");  
20.         updateLastLogonTime(userName);  
21. "after userService.updateLastLogonTime method...");  
22.   
23. //scoreService.addScore(userName, 20);//①在同一线程中调用scoreService#addScore()
24.           
25. //②在一个新线程中执行scoreService#addScore()
26. new MyThread(this.scoreService, userName, 20);//使用一个新线程运行
27.         myThread.start();  
28.     }  
29.   
30. public void
31. "UPDATE t_user u SET u.last_logon_time = ? WHERE user_name =?";  
32.         jdbcTemplate.update(sql, System.currentTimeMillis(), userName);  
33.     }  
34.       
35. //③负责执行scoreService#addScore()的线程类
36. private class MyThread extends
37. private
38. private
39. private int
40. private MyThread(ScoreService scoreService, String userName, int
41. this.scoreService = scoreService;  
42. this.userName = userName;  
43. this.toAdd = toAdd;  
44.         }  
45.   
46. public void
47. try
48. 2000);  
49. catch
50.                 e.printStackTrace();  
51.             }  
52. "before scoreService.addScor method...");  
53.             scoreService.addScore(userName, toAdd);  
54. "after scoreService.addScor method...");  
55.         }  
56.     }  
57. }


引用


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()方法的事务在③处启动,在④处对应的事务提交。 


   所以,我们可以得出这样的结论:在相同线程中进行相互嵌套调用的事务方法工作于相同的事务中。如果这些相互嵌套调用的方法工作在不同的线程中,则不同线程下的事务方法工作在独立的事务中。