快捷搜索:

Spring事务管理高级应用难点剖析(4)

多线程的利诱

因为Spring事务治理器是经由过程线程相关的ThreadLocal来保存数据造访根基举措措施,再结合IOC和AOP实现高档声明式事务的功能,以是Spring的事务天然地和线程有着千丝万缕的联系。

我们知道Web容器本身便是多线程的,Web容器为一个Http哀求创建一个自力的线程,以是由此哀求所扳连到的Spring容器中的Bean也是运行于多线程的情况下。在绝大年夜多半环境下,Spring的Bean都是单实例的(singleton),单实例Bean的最大年夜的好处是线程无关性,不存在多线程并发造访的问题,也等于线程安然的。一个类能够以单实例的要领运行的条件是“无状态”:即一个类不能拥有状态化的成员变量。我们知道,在传统的编程中,DAO必须执有一个Connection,而Connection等于状态化的工具。以是传统的DAO不能做成单实例的,每次要用时都必须new一个新的实例。传统的Service因为将有状态的DAO作为成员变量,以是传统的Service本身也是有状态的。

然则在Spring中,DAO和Service都以单实例的要领存在。Spring是经由过程ThreadLocal将有状态的变量(如Connection等)本地线程化,达到另一个层面上的“线程无关”,从而实现线程安然。Spring竭尽全力地将状态化的工具无状态化,便是要达到单实例化Bean的目的。因为Spring已经经由过程ThreadLocal的举措措施将Bean无状态化,以是Spring中单实例Bean对线程安然问题拥有了一种生成的免疫能力。不只单实例的Service可以成功运行于多线程情况中,Service本身还可以自由地启动自力线程以履行其它的Service。下面,经由过程一个实例对此进行描述:

清单13UserService.java在事务措施中启动自力线程运行另一个事务措施

40.@Service("userService")

41.publicclassUserServiceextendsBaseService{

42.@Autowired

43.privateJdbcTemplatejdbcTemplate;

44.

45.@Autowired

46.privateScoreServicescoreService;

47.//①在logon措施体中启动一个自力的线程,在该自力的线程中履行ScoreService#addScore()措施

48.publicvoidlogon(StringuserName){

49.System.out.println("logonmethod...");

50.updateLastLogonTime(userName);

51.ThreadmyThread=newMyThread(this.scoreService,userName,20);

52.myThread.start();

53.}

54.

55.publicvoidupdateLastLogonTime(StringuserName){

56.System.out.println("updateLastLogonTime...");

57.Stringsql="UPDATEt_useruSETu.last_logon_time=?WHEREuser_name=?";

58.jdbcTemplate.update(sql,System.currentTimeMillis(),userName);

59.}

60.//②封装ScoreService#addScore()的线程

61.privateclassMyThreadextendsThread{

62.privateScoreServicescoreService;

63.privateStringuserName;

64.privateinttoAdd;

65.privateMyThread(ScoreServicescoreService,StringuserName,inttoAdd){

66.this.scoreService=scoreService;

67.this.userName=userName;

68.this.toAdd=toAdd;

69.}

70.publicvoidrun(){

71.scoreService.addScore(userName,toAdd);

72.}

73.}

74.}

将日志级别设置为DEBUG,履行UserService#logon()措施,察看以下输出的日志:

清单14履行日志

75.[main](AbstractPlatformTransactionManager.java:365)-Creatingnewtransactionwithname

76.[user.multithread.UserService.logon]:PROPAGATION_REQUIRED,ISOLATION_DEFAULT①

77.

78.[main](DataSourceTransactionManager.java:205)-AcquiredConnection

79.[org.apache.commons.dbcp.PoolableConnection@1353249]forJDBCtransaction

80.

81.logonmethod...

82.

83.updateLastLogonTime...

84.

85.[main](JdbcTemplate.java:785)-ExecutingpreparedSQLupdate

86.[main](JdbcTemplate.java:569)-ExecutingpreparedSQLstatement

87.[UPDATEt_useruSETu.last_logon_time=?WHEREuser_name=?]

88.[main](JdbcTemplate.java:794)-SQLupdateaffected0rows

89.[main](AbstractPlatformTransactionManager.java:752)-Initiatingtransactioncommit

90.

91.[Thread-2](AbstractPlatformTransactionManager.java:365)-

92.Creatingnewtransactionwithname[user.multithread.ScoreService.addScore]:

93.PROPAGATION_REQUIRED,ISOLATION_DEFAULT②

94.[main](DataSourceTransactionManager.java:265)-CommittingJDBCtransaction

95.onConnection[org.apache.commons.dbcp.PoolableConnection@1353249]③

96.

97.[main](DataSourceTransactionManager.java:323)-ReleasingJDBCConnection

98.[org.apache.commons.dbcp.PoolableConnection@1353249]aftertransaction

99.[main](DataSourceUtils.java:312)-ReturningJDBCConnectiontoDataSource

100.

101.[Thread-2](DataSourceTransactionManager.java:205)-AcquiredConnection

102.[org.apache.commons.dbcp.PoolableConnection@10dc656]forJDBCtransaction

103.

104.addScore...

105.

106.[main](JdbcTemplate.java:416)-ExecutingSQLstatement

107.[DELETEFROMt_userWHEREuser_name='tom']

108.[main](DataSourceUtils.java:112)-FetchingJDBCConnectionfromDataSource

109.[Thread-2](JdbcTemplate.java:785)-ExecutingpreparedSQLupdate

110.[Thread-2](JdbcTemplate.java:569)-ExecutingpreparedSQLstatement

111.[UPDATEt_useruSETu.score=u.score+?WHEREuser_name=?]

112.[main](DataSourceUtils.java:312)-ReturningJDBCConnectiontoDataSource

113.[Thread-2](JdbcTemplate.java:794)-SQLupdateaffected0rows

114.[Thread-2](AbstractPlatformTransactionManager.java:752)-Initiatingtransactioncommit

115.[Thread-2](DataSourceTransactionManager.java:265)-CommittingJDBCtransaction

116.onConnection[org.apache.commons.dbcp.PoolableConnection@10dc656]④

117.[Thread-2](DataSourceTransactionManager.java:323)-ReleasingJDBCConnection

118.[org.apache.commons.dbcp.PoolableConnection@10dc656]aftertransaction

在①处,在主线程(main)履行的UserService#logon()措施的事务启动,在③处,其对应的事务提交,而在子线程(Thread-2)履行的ScoreService#addScore()措施的事务在②处启动,在④处对应的事务提交。

以是,我们可以得出这样的结论:在相同线程中进行互相嵌套调用的事务措施事情于相同的事务中。假如这些互相嵌套调用的措施事情在不合的线程中,不合线程下的事务措施事情在自力的事务中。

小结

Spring声明式事务是Spring最核心,最常用的功能。因为Spring经由过程IOC和AOP的功能异常透明地实现了声明式事务的功能,一样平常的开拓者基础上无须懂得Spring声明式事务的内部细节,仅必要相识若何设置设置设备摆设摆设就可以了。

然则在实际利用开拓历程中,Spring的这种透明的高阶封装在带来便利的同时,也给我们带来了迷惑。就像经由过程流言传播的消息,终极听众已经不清楚工作的本相了,而这对付利用开拓来说是很危险的。本系列文章经由过程剖析实际利用中给开拓者造成迷惑的各类难点,经由过程阐发Spring事务治理的内部运作机制将本相还原出来。在本文中,我们经由过程剖析懂得到以下的本相:

◆在没有事务治理的环境下,DAO照旧可以顺利进行数据操作;

◆将利用分成Web,Service及DAO层只是一种参考的开拓模式,并非是事务治理事情的条件前提;

◆Spring经由过程事务传播机制可以很好地应对事务措施嵌套调用的环境,开拓者无须为了事务治理而克意改变办事措施的设计;

◆因为单实例的工具不存在线程安然问题,以是进行事务治理增强的Bean可以很好地事情在多线程情况下。

◆混杂应用多种数据造访技巧(如SpringJDBC+Hibernate)的事务治理问题;

◆在经由过程Bean的措施经由过程SpringAOP增强存在哪些特殊的环境。

您可能还会对下面的文章感兴趣: