熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java開源技術 >> 正文

Spring事務管理高級應用難點剖析(4)

2022-06-13   來源: Java開源技術 

  多線程的困惑

  由於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下面通過一個實例對此進行描述

  清單UserServicejava在事務方法中啟動獨立線程運行另一個事務方法

  @Service(userService)

  publicclassUserServiceextendsBaseService{

  @Autowired

  privateJdbcTemplatejdbcTemplate;

  

  @Autowired

  privateScoreServicescoreService;

  //①在logon方法體中啟動一個獨立的線程在該獨立的線程中執行ScoreService#addScore()方法

  publicvoidlogon(StringuserName){

  Systemoutprintln(logonmethod);

  updateLastLogonTime(userName);

  ThreadmyThread=newMyThread(thisscoreServiceuserName);

  myThreadstart();

  }

  

  publicvoidupdateLastLogonTime(StringuserName){

  Systemoutprintln(updateLastLogonTime);

  Stringsql=UPDATEt_useruSETulast_logon_time=?WHEREuser_name=?;

  jdbcTemplateupdate(sqlSystemcurrentTimeMillis()userName);

  }

  //②封裝ScoreService#addScore()的線程

  privateclassMyThreadextendsThread{

  privateScoreServicescoreService;

  privateStringuserName;

  privateinttoAdd;

  privateMyThread(ScoreServicescoreServiceStringuserNameinttoAdd){

  thisscoreService=scoreService;

  thisuserName=userName;

  thistoAdd=toAdd;

  }

  publicvoidrun(){

  scoreServiceaddScore(userNametoAdd);

  }

  }

  }

  將日志級別設置為DEBUG執行UserService#logon()方法觀察以下輸出的日志

  清單執行日志

  [main](AbstractPlatformTransactionManagerjava:)Creatingnewtransactionwithname

  [usermultithreadUserServicelogon]:PROPAGATION_REQUIREDISOLATION_DEFAULT①

  

  [main](DataSourceTransactionManagerjava:)AcquiredConnection

  [monsdbcpPoolableConnection@]forJDBCtransaction

  

  logonmethod

  

  updateLastLogonTime

  

  [main](JdbcTemplatejava:)ExecutingpreparedSQLupdate

  [main](JdbcTemplatejava:)ExecutingpreparedSQLstatement

  [UPDATEt_useruSETulast_logon_time=?WHEREuser_name=?]

  [main](JdbcTemplatejava:)SQLupdateaffectedrows

  [main](AbstractPlatformTransactionManagerjava:)Initiatingtransactioncommit

  

  [Thread](AbstractPlatformTransactionManagerjava:)

  Creatingnewtransactionwithname[usermultithreadScoreServiceaddScore]:

  PROPAGATION_REQUIREDISOLATION_DEFAULT②

  [main](DataSourceTransactionManagerjava:)CommittingJDBCtransaction

  onConnection[monsdbcpPoolableConnection@]③

  

  [main](DataSourceTransactionManagerjava:)ReleasingJDBCConnection

  [monsdbcpPoolableConnection@]aftertransaction

  [main](DataSourceUtilsjava:)ReturningJDBCConnectiontoDataSource

  

  [Thread](DataSourceTransactionManagerjava:)AcquiredConnection

  [monsdbcpPoolableConnection@dc]forJDBCtransaction

  

  addScore

  

  [main](JdbcTemplatejava:)ExecutingSQLstatement

  [DELETEFROMt_userWHEREuser_name=tom]

  [main](DataSourceUtilsjava:)FetchingJDBCConnectionfromDataSource

  [Thread](JdbcTemplatejava:)ExecutingpreparedSQLupdate

  [Thread](JdbcTemplatejava:)ExecutingpreparedSQLstatement

  [UPDATEt_useruSETuscore=uscore+?WHEREuser_name=?]

  [main](DataSourceUtilsjava:)ReturningJDBCConnectiontoDataSource

  [Thread](JdbcTemplatejava:)SQLupdateaffectedrows

  [Thread](AbstractPlatformTransactionManagerjava:)Initiatingtransactioncommit

  [Thread](DataSourceTransactionManagerjava:)CommittingJDBCtransaction

  onConnection[monsdbcpPoolableConnection@dc]④

  [Thread](DataSourceTransactionManagerjava:)ReleasingJDBCConnection

  [monsdbcpPoolableConnection@dc]aftertransaction

  在①處在主線程(main)執行的UserService#logon()方法的事務啟動在③處其對應的事務提交而在子線程(Thread)執行的ScoreService#addScore()方法的事務在②處啟動在④處對應的事務提交

  所以我們可以得出這樣的結論在相同線程中進行相互嵌套調用的事務方法工作於相同的事務中如果這些相互嵌套調用的方法工作在不同的線程中不同線程下的事務方法工作在獨立的事務中

  小結

  Spring聲明式事務是Spring最核心最常用的功能由於Spring通過IOC和AOP的功能非常透明地實現了聲明式事務的功能一般的開發者基本上無須了解Spring聲明式事務的內部細節僅需要懂得如何配置就可以了

  但是在實際應用開發過程中Spring的這種透明的高階封裝在帶來便利的同時也給我們帶來了迷惑就像通過流言傳播的消息最終聽眾已經不清楚事情的真相了而這對於應用開發來說是很危險的本系列文章通過剖析實際應用中給開發者造成迷惑的各種難點通過分析Spring事務管理的內部運作機制將真相還原出來在本文中我們通過剖析了解到以下的真相

  ◆在沒有事務管理的情況下DAO照樣可以順利進行數據操作

  ◆將應用分成WebService及DAO層只是一種參考的開發模式並非是事務管理工作的前提條件

  ◆Spring通過事務傳播機制可以很好地應對事務方法嵌套調用的情況開發者無須為了事務管理而刻意改變服務方法的設計

  ◆由於單實例的對象不存在線程安全問題所以進行事務管理增強的Bean可以很好地工作在多線程環境下

  ◆混合使用多種數據訪問技術(如SpringJDBC+Hibernate)的事務管理問題

  ◆在通過Bean的方法通過SpringAOP增強存在哪些特殊的情況


From:http://tw.wingwit.com/Article/program/Java/ky/201311/27939.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.