问题导读

  • Spring中事务是如何实现的
  • Spring中各个线程间是如何进行连接、事务隔离的

Spring事务配置

Spring的事务管理应该是日常开发中总会碰到的,但是Spring具体是怎么实现线程间的事务隔离的,下面我们就最基本的DataSourceTransactionMnager来看下。

一般使用的是以下的方式配置transaction(当然还有其他aop等方式)

  1. <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  2. <property name="dataSource" ref="dataSource" />
  3. </bean>
  4. <tx:annotation-driven proxy-target-class="true" transaction-manager="txManager"/>

将datasource注入transactionManager之后注册tx:annotation-driven内就可以在代码中使用注解Transactional进行定义事务了

这里是Spring在启动时将transactionManager的代码织入业务代码来实现事务管理(后续会研究如何织入的)。

getTransaction

当调用到相关业务代码前首先调用AbstractPlatformTransactionManager 的getTransaction这个方法会控制事务的传播级别(require,requirenew,support。。。)

  1. public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
  2. Object transaction = doGetTransaction(); //获得现有transaction
  3. // Cache debug flag to avoid repeated checks.
  4. boolean debugEnabled = logger.isDebugEnabled();
  5. if (definition == null) {
  6. // Use defaults if no transaction definition given.
  7. definition = new DefaultTransactionDefinition();
  8. }
  9. if (isExistingTransaction(transaction)) {
  10. // 如果当前transaction存在就使用handleExistingTransaction,这里是主要控制事务传播机制的地方
  11. return handleExistingTransaction(definition, transaction, debugEnabled);
  12. }
  13. // Check definition settings for new transaction.
  14. if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
  15. throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
  16. }
  17. // 当前无transaction存在,根据事务传播级别进行控制
  18. if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
  19. throw new IllegalTransactionStateException(
  20. "No existing transaction found for transaction marked with propagation 'mandatory'");
  21. }
  22. else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
  23. definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
  24. definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
  25. SuspendedResourcesHolder suspendedResources = suspend(null);
  26. if (debugEnabled) {
  27. logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
  28. }
  29. try {
  30. boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
  31. DefaultTransactionStatus status = newTransactionStatus(
  32. definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
  33. //真正开始事务
  34. doBegin(transaction, definition);
  35. //将事务状态存入TransactionSynchronizationManager
  36. prepareSynchronization(status, definition);
  37. return status;
  38. }
  39. catch (RuntimeException ex) {
  40. resume(null, suspendedResources);
  41. throw ex;
  42. }
  43. catch (Error err) {
  44. resume(null, suspendedResources);
  45. throw err;
  46. }
  47. }
  48. else {
  49. // Create "empty" transaction: no actual transaction, but potentially synchronization.
  50. if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
  51. logger.warn("Custom isolation level specified but no actual transaction initiated; " +
  52. "isolation level will effectively be ignored: " + definition);
  53. }
  54. boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
  55. return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
  56. }
  57. }
  1. //获得当前transaction(如果有)
  2. protected Object doGetTransaction() {
  3. DataSourceTransactionObject txObject = new DataSourceTransactionObject();
  4. txObject.setSavepointAllowed(isNestedTransactionAllowed());
  5. //从TransactionSynchronizationManager中获得当前线程中的Connection
  6. ConnectionHolder conHolder =
  7. (ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);
  8. txObject.setConnectionHolder(conHolder, false);
  9. return txObject;
  10. }
  11. ...
  12. //开始事务
  13. protected void doBegin(Object transaction, TransactionDefinition definition) {
  14. DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
  15. Connection con = null;
  16. try {
  17. if (txObject.getConnectionHolder() == null ||
  18. txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
  19. //从配置中注入的datasource中获得connection
  20. Connection newCon = this.dataSource.getConnection();
  21. if (logger.isDebugEnabled()) {
  22. logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
  23. }
  24. txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
  25. }
  26. txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
  27. con = txObject.getConnectionHolder().getConnection();
  28. Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
  29. txObject.setPreviousIsolationLevel(previousIsolationLevel);
  30. // Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
  31. // so we don't want to do it unnecessarily (for example if we've explicitly
  32. // configured the connection pool to set it already).
  33. if (con.getAutoCommit()) {
  34. txObject.setMustRestoreAutoCommit(true);
  35. if (logger.isDebugEnabled()) {
  36. logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
  37. }
  38. //设置非自动提交
  39. con.setAutoCommit(false);
  40. }
  41. txObject.getConnectionHolder().setTransactionActive(true);
  42. int timeout = determineTimeout(definition);
  43. if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
  44. txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
  45. }
  46. // Bind the session holder to the thread.
  47. if (txObject.isNewConnectionHolder()) {
  48. //将新建的Connection放入TransactionSynchronizationManager
  49. TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
  50. }
  51. }
  52. catch (Throwable ex) {
  53. if (txObject.isNewConnectionHolder()) {
  54. DataSourceUtils.releaseConnection(con, this.dataSource);
  55. txObject.setConnectionHolder(null, false);
  56. }
  57. throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
  58. }
  59. }
  60. protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
  61. if (status.isNewSynchronization()) {
  62. //将transaction的状态保存到TransactionSynchronizationManager
  63. TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
  64. TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
  65. definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
  66. definition.getIsolationLevel() : null);
  67. TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
  68. TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
  69. TransactionSynchronizationManager.initSynchronization();
  70. }
  71. }
  1. public abstract class TransactionSynchronizationManager {
  2. //ThreadLocal的resources用来保存TransactionStatus Connection等
  3. private static final ThreadLocal<Map<Object, Object>> resources =
  4. new NamedThreadLocal<>("Transactional resources");
  5. ...
  6. public static Object getResource(Object key) {
  7. Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
  8. Object value = doGetResource(actualKey);
  9. if (value != null && logger.isTraceEnabled()) {
  10. logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" +
  11. Thread.currentThread().getName() + "]");
  12. }
  13. return value;
  14. }

可以看到其实Spring只是简单的将获得的连接和事务信息存放到TransactionSynchronizationManager中的ThreadLoacl变量中进行保存,这样就实现了数据库连接及事务的线程安全。

Commit/Rollback

在Commit/rollback阶段是使用从一开始getTransaction方法返回的TransactionStatus(其中存放了connection和transaction的信息)作为参数传入commit/rollback进行commit/rollback并在finally中清理TransactionSynchronizationManager

  1. //commit部分代码
  2. private void processCommit(DefaultTransactionStatus status) throws TransactionException {
  3. try {
  4. boolean beforeCompletionInvoked = false;
  5. try {
  6. prepareForCommit(status);
  7. triggerBeforeCommit(status);
  8. triggerBeforeCompletion(status);
  9. beforeCompletionInvoked = true;
  10. boolean globalRollbackOnly = false;
  11. if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
  12. globalRollbackOnly = status.isGlobalRollbackOnly();
  13. }
  14. if (status.hasSavepoint()) {
  15. if (status.isDebug()) {
  16. logger.debug("Releasing transaction savepoint");
  17. }
  18. status.releaseHeldSavepoint();
  19. }
  20. //如果是自己创建的事物就进行提交,如果不是(比如是嵌套的)就由上层提交
  21. else if (status.isNewTransaction()) {
  22. if (status.isDebug()) {
  23. logger.debug("Initiating transaction commit");
  24. }
  25. doCommit(status);
  26. }
  27. // Throw UnexpectedRollbackException if we have a global rollback-only
  28. // marker but still didn't get a corresponding exception from commit.
  29. if (globalRollbackOnly) {
  30. throw new UnexpectedRollbackException(
  31. "Transaction silently rolled back because it has been marked as rollback-only");
  32. }
  33. ...
  34. }
  35. finally {
  36. //清除当前TransactionSynchronizationManager中transaction状态和释放申请的连接
  37. cleanupAfterCompletion(status);
  38. }
  39. }

小结

Spring内部维护了TransactionSynchronizationManager一个单例,并使用ThreadLocal变量记录所有连接事务的信息,这样就防止了线程之间事务、连接的共享,从而实现事务的隔离

Spring Trasnaction管理(1)- 线程间事务隔离的更多相关文章

  1. Spring Trasnaction管理(3)- 事务嵌套

    问题导读 Spring 如何管理嵌套的事务 Spring事务传播机制 Nested 和 RequireNew 有何区别 事务传播机制 事务的传播机制应该都比较熟悉 在日常开发中会遇到需要事务嵌套的情况 ...

  2. Spring Trasnaction管理(2)- 事务AOP

    问题导读 spring AOP是在如何进行的 spring 用cglib和jdkProxy管理的事务有何区别 Spring AOP管理 Spring主要的两个核心功能IOC与AOP.IOC的代码解析可 ...

  3. Java多线程编程核心技术---线程间通信(二)

    通过管道进行线程间通信:字节流 Java提供了各种各样的输入/输出流Stream可以很方便地对数据进行操作,其中管道流(pipeStream)是一种特殊的流,用于在不同线程间直接传送数据,一个线程发送 ...

  4. java多线程编程核心技术(三)--线程间通信

    1.等待/通知机制 1.wait()方法:使当前执行代码的线程进行等待.wait()方法是Object类的方法,该方法将当前线程放入“预执行队列”中,并在wait()所处的代码行处停止执行.只到被唤起 ...

  5. Java多线程编程(三)线程间通信

    线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体.线程间的通信就是成为整体的必用方案之一,可以说,使线程间进行通信后,系统之间的交互性会更强大,在大大提高CPU利用率的同时 ...

  6. Java多线程——线程间通信

    Java多线系列文章是Java多线程的详解介绍,对多线程还不熟悉的同学可以先去看一下我的这篇博客Java基础系列3:多线程超详细总结,这篇博客从宏观层面介绍了多线程的整体概况,接下来的几篇文章是对多线 ...

  7. Java多线程编程核心技术-第3章-线程间通信-读书笔记

    第 3 章 线程间通信 线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体.线程间的通信就是成为整体的必用方案之一,可以说,使线程间进行通信后,系统之间的交互性会更强大,在大 ...

  8. Java——多线程之线程间通信

    Java多线系列文章是Java多线程的详解介绍,对多线程还不熟悉的同学可以先去看一下我的这篇博客Java基础系列3:多线程超详细总结,这篇博客从宏观层面介绍了多线程的整体概况,接下来的几篇文章是对多线 ...

  9. 事务隔离级别与传播机制,spring+mybatis+atomikos实现分布式事务管理

    1.事务的定义:事务是指多个操作单元组成的合集,多个单元操作是整体不可分割的,要么都操作不成功,要么都成功.其必须遵循四个原则(ACID). 原子性(Atomicity):即事务是不可分割的最小工作单 ...

随机推荐

  1. C# Susan边缘检测(Susan Edge Detection)

    Susan边缘检测,方法简单,效率高,具体参照 The SUSAN Edge Detector in Detail, 修改dThreshold值,可以改变检测效果,用参照提供的重心法.力矩法可得到边缘 ...

  2. 误打误撞写了段能让电脑奔溃的JS代码,但是自己不知道为什么,高手看到可以解答下吗?

    代码如下: <script> for(i=1;j=3*i;i++){ for(;j<=50;){ document.write(j+"<br>") } ...

  3. Java编程中“为了性能”尽量要做到的一些地方

    最近的机器内存又爆满了,除了新增机器内存外,还应该好好review一下我们的代码,有很多代码编写过于随意化,这些不好的习惯或对程序语言的不了解是应该好好打压打压了. 下面是参考网络资源总结的一些在Ja ...

  4. RestController 和Controller的区别

    restful风格,restcontroller与controller 初步接触springmvc的时候,被要求使用restful风格,彼时一头雾水,不懂何谓restful,参阅了很多资料,慢慢的接触 ...

  5. javascript实现字符串的截取

    截取字符串方法有很多的,(不含根据传入参数截取成数组的split()方法)这里说的是子字符串,所以不说split()方法了. slice(),substr(),substring()一共三种方法,其中 ...

  6. web工具网站等

    框架 1.handlebars http://handlebarsjs.com/ 2.http://underscorejs.org/#keys 3.http://stylus-lang.com/ 4 ...

  7. Debian 8下vsftpd安装与配置

    Debian 8下vsftpd安装与配置 0.环境 root@remote:/# uname -r 3.16.0-4-amd64 root@remote:/e# lsb_release No LSB ...

  8. android 的数学公式图片转换

    在应用中的数学公式是不能直接以文本显示和输入的,包括在一些学习类网站上看到的公式,他们都是以gif图片的形式展示出来的.而怎么样生成各种各样的gif图片形式的数学公式呢,此处未作深入研究,我所知道的是 ...

  9. ArcGIS API ArcGISDynamicMapServiceLayer.setVisibleLayers对带有GroupLayer图层组的数据无效(针对LayerInfo)问题探讨

    首先看下setVisibleLayers方法: setVisibleLayers(ids, doNotRefresh?) Sets the visible layers of the exported ...

  10. 原生JavaScript实现滚动条

    没事找事,明明overflow:scroll|auto就可以,只是难看点(实际上css也能设置).只当练习写拖拽.监听事件.位置检测了. 原理是对滑动条块进行监听,按下鼠标按键后,监听鼠标移动,然后根 ...