要想了解Spring的事务,首先要了解数据库事务的基本知识,数据库并发会产生很多问题,Spring使用ThreadLocal技术来处理这些问题,那么我们必须了解Java的ThreadLocal技术。下面我们逐一了解。

第一回合:数据库事务的基本知识

什么是数据库事务?

一次执行多个SQL语句,全部执行成功则成功,有一个执行失败则全部失败。即“一荣俱荣,一损俱损”。

数据库的事务必须同时满足下列四个条件:

原子性(Atomic:比如数据库一次执行四个SQL语句,那么这四个SQL就是宏观的一个不可分割单元,“一荣俱荣,一损俱损”。全部执行成功则成功,有一个执行失败则全部失败。三分归元气。

一致性(Consistency:整个事务不管成功了还是失败了,整个数据库的状态和规则不能变化。即:A账户转账100元到B账户,这个事务过程结束后前后,数据库中总的账户金额是不变的。

隔离性(Isolation:不同的事务并发执行时,各自拥有不同的数据空间,你走你的阳关道,我走我的独木桥,互不干扰。

但并非完全不干扰,数据库规定了事务隔离级别,隔离级别越高,数据的一致性越好,并发性越弱。

持久性(Durability:一旦事务提交成功,事务中的所有数据操作都必须被持久化到数据库中。

这样一来,即使刚提交完,数据库就崩溃,当重启数据库之后,也可以根据已经保存(持久化)的操作来恢复数据。

一致性是结果,其他三个是手段。

  • 数据库管理一般采用重执行日志保证原子性、一致性和持久性。
  • 重执行日志记录了数据库变化的每一个动作。这样,即使数据库事务在执行了一部分操作后发生错误退出,可以根据重执行日志来撤销已经执行的操作。
  • 对于已经提交的事务,即使数据库崩溃,再重启数据库时也能够根据日志对尚未持久化的数据进行相应的重执行操作。
  • 数据库管理系统采用数据库锁机制来保证事务的隔离性(正如Java采用对象锁机制进行线程同步。)

数据并发的问题

多个客户端同时操作一个数据库,该并发过程就可能引起并发问题:

脏读(dirty read):A事务读取B事务尚未提交的数据并进行一系列操作,结果B事务执行了回滚,那么这时,A事务读到的数据就是不被认可的,是脏数据。

不可重复读(unrepeatable read):比如:A开始了查询事务,B开始了提款事务。A第一次查询余额为1000元,这时B提取100元,A第二次查询余额时,变成了900元,与第一次查询的余额不同。

幻象读(phantom read:一般发生在计算统计数据的事务中。比如;银行正在统计所有账户的存款总额,统计出来为10000元。这时正好新增了一个账户,存款1000元。再次统计发现总额为11000元,与前一次统计不同。

不可重复读是指读到了更改的数据(一般情况下需要添加行级锁,阻止操作中的数据变化),而幻象读是指读到了新增的数据(往往需要添加表级锁,将整个表锁定)。

第一类丢失更新:目前账户余额1000元,A开始事务-->B开始事务-->B汇入100元,余额改为1100元-->B提交事务àA取出100元,把余额改为900元-->A撤销事务-->余额恢复为1000元(丢失更新)。

A事务撤销时,把B提交的更新数据给覆盖了。

第二类丢失更新:B开始事务-->A开始事务-->B查询余额为1000元-->A查询余额为1000元-->B取出100元,把余额改为900元-->B提交事务-->A汇入100元-->A提交事务-->A把余额改为1100元(丢失更新)。

  A在提交事务时,把B所做的操作丢失。

JDBC对事务的支持

Connection默认情况下是自动提交的。

为了把多个事务当成一个事务执行,就必须强制阻止自动提交(第五行)。

第二回合:ThreadLocal

l  Spring通过各种模板类降低了开发者使用各种持久技术的难度。

l  这些模板类都是线程安全的。

l  模板类需要绑定数据连接或者会话的资源。

l  这些资源本身是非线程安全的。

l  虽然模板类通过资源池获取连接或者会话,

l  但是资源池解决的是数据连接或者资源的缓存问题,

l  而不是线程安全问题。

l  按照惯例,采用synchronized进行线程同步。

l  但是该线程同步机制解决具体问题时,开发难度大、降低并发性、影响系统性能。

l  所以,模板类并未采用线程同步机制。

l  那么,模板类究竟采用什么方式保证线程安全的呢?

l  答案:ThreadLocal!

ThreadLocal是什么?

ThreadLocal,顾名思义,它不是一个线程,而是线程的一个本地化对象。多线程程序使用ThreadLocal维护变量时,每一个线程将拿到该变量的一个副本,从而,每个线程对各自变量的副本的更改都不会影响到其他线程。

一个ThreadLocal实例

上例很简单,三个线程都拿到Integer对象的副本,该Integer对象的初始化值设置为0,然后各自修改,互不影响。

除了set、get、initialValue之外,ThreadLocal还有一个方法:remove(),该方法将当前变量副本从该线程中删除,减少内存的占用。

与Thread同步机制的比较

  • 在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量,该变量是多个线程共享的,那么,每个线程在什么时候可以对变量读写,什么时候要对该对象加锁,什么时候释放对象锁等,都要准确判断,逻辑复杂,编写难度大。
  • ThreadLoacl为每一个线程提供一个变量的副本,隔离了多线程访问数据的冲突。ThreadLocal提供了线程安全的对象封装,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。

  总之,对多线程共享的问题,同步机制采用了”以时间换空间,访问串行化,对象共享化”。而ThreadLocal则是“以空间换时间,访问并行化,对象独享化。前者只提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

Spring与ThreadLocal

有状态就是有数据存储功能。有状态对象(Stateful Bean),就是有实例变量的对象,可以保存数据,是非线程安全的。在不同方法调用间不保留任何状态。

无状态就是一次操作,不能保存数据。无状态对象(Stateless Bean),就是没有实例变量的对象.不能保存数据,是不变类,是线程安全的。

一般情况下,只有无状态bean才可以在多线程环境下共享(既然没有状态,不能保存数据,随便共享啦)

在spring中,绝大部分Bean都可以声明为singleton作用域。(如果在<bean>中指定Bean的作用范围是scopt="prototype",那么系统将bean返回给调用者,spring就不管了(如果两个实例调用的话,每一次调用都要重新初始化,一个实例的修改不会影响另一个实例的值。如果指定Bean的作用范围是scope="singleton",则把bean放到缓冲池中,并将bean的引用返回给调用者。这个时候,如果两个实例调用的话,因为它们用的是同一个引用,任何一方的修改都会影响到另一方。))

正因为Spring对一些Bean(RequestContextholder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全的”状态性对象”采用ThreadLocal封装,让它们成为线程安全的”状态性对象”,因此有状态的bean就能够以singleton方式在多线程中正常工作了。

Spring对有状态bean的改造思路

非线程安全:

由于第8行的conn是非线程安全的成员变量,

因此addTopic()方法也是非线程安全的,

每次使用时都必须新创建一个TopicDao实例(非singleton)。

对非线程安全的conn进行改造:

上例仅为了简单说明原理,并不做深究,例子粗糙,并不能在实际环境中使用,还有很多要考虑的其他问题。

第三回合:Spring对事务管理的支持

  • 不管选择Spring JDBC,Hibernate,JPA还是iBatis,Spring都让我们可以用统一的编程模型进行事务管理。
  • Spring事务管理有几个主要的抽象父类,在事务管理的运作过程中各司其职,主要的功能:

     描述事务的隔离级别、超时时间、是否只读等。

    定义事务的属性,比如事务隔离(当前事务与其他事务的隔离程度)、事务传播、事务超时、只读状态等。

    描述事务的具体运行状态。

  • 对应不同的持久化技术,Spring事务管理封装了具体的实现类。每一种实现类对应的配置方式有所不同。
  • Spring使用ThreadLocal技术给不同线程提供各自的数据连接副本。
  • Spring通过事务传播行为来处理事务嵌套调用时的运作。
  • Spring声明式事务管理是通过AOP实现的,通过声明性信息,Spring负责将事务管理增强逻辑动态织入到业务方法的连接点中。这些逻辑包括:获取线程绑定资源、开始事务、提交/回滚事务、进行异常转换和处理等。
  • 基于tx/aop命名空间配置事务:在XML中配置目标类、事务管理器、增强类、定义切面,引入增强等。
  • 使用注解配置声明式事务:@Transactional。

(spring-第20回【AOP基础篇】)Spring与事务的更多相关文章

  1. Spring+SpringMVC+MyBatis+easyUI整合基础篇(六)maven整合SSM

    写在前面的话   承接前文<Spring+SpringMVC+MyBatis+easyUI整合基础篇(五)讲一下maven>,本篇所讲述的是如何使用maven与原ssm项目整合,使得一个普 ...

  2. Spring+SpringMVC+MyBatis+easyUI整合基础篇(八)mysql中文查询bug修复

    写在前面的话 在测试搜索时出现的问题,mysql通过中文查询条件搜索不出数据,但是英文和数字可以搜索到记录,中文无返回记录.本文就是写一下发现问题的过程及解决方法.此bug在第一个项目中点这里还存在, ...

  3. Spring+SpringMVC+MyBatis+easyUI整合基础篇(十一)SVN服务器进阶

    日常啰嗦 上一篇文章<Spring+SpringMVC+MyBatis+easyUI整合基础篇(十)SVN搭建>简单的讲了一下SVN服务器的搭建,并没有详细的介绍配置文件及一些复杂的功能, ...

  4. Spring+SpringMVC+MyBatis+easyUI整合基础篇(十二)阶段总结

    不知不觉,已经到了基础篇的收尾阶段了,看着前面的十几篇文章,真的有点不敢相信,自己竟然真的坚持了下来,虽然过程中也有过懒散和焦虑,不过结果还是自己所希望的,克服了很多的问题,将自己的作品展现出来,也发 ...

  5. Spring+SpringMVC+MyBatis+easyUI整合基础篇

    基础篇 Spring+SpringMVC+MyBatis+easyUI整合基础篇(一)项目简介 Spring+SpringMVC+MyBatis+easyUI整合基础篇(二)牛刀小试 Spring+S ...

  6. Spring cloud系列教程第十篇- Spring cloud整合Eureka总结篇

    Spring cloud系列教程第十篇- Spring cloud整合Eureka总结篇 本文主要内容: 1:spring cloud整合Eureka总结 本文是由凯哥(凯哥Java:kagejava ...

  7. Spring基础篇——Spring的AOP切面编程

    一  基本理解 AOP,面向切面编程,作为Spring的核心思想之一,度娘上有太多的教程啊.解释啊,但博主还是要自己按照自己的思路和理解再来阐释一下.原因很简单,别人的思想终究是别人的,自己的理解才是 ...

  8. Spring+SpringMVC+MyBatis+easyUI整合基础篇(一)项目简介

    很久之前就打算开始写一下自己的技术博客了,实在抽不出时间所以计划一直搁置了,最近项目进度渐渐缓了下来,不那么忙了,也因此开始筹备自己的博客.说到这次博客的主角,也是无心插柳找到的,来源于两年前自己写的 ...

  9. 【SSM之旅】Spring+SpringMVC+MyBatis+Bootstrap整合基础篇(一)项目简介及技术选型相关介绍

    试水 一直想去搭建个自己的个人博客,苦于自己的技术有限,然后也个人也比较懒散.想动而不能动,想动而懒得动,就这么一直拖到了现在.总觉得应该把这几年来的所学总结一番,这样才能有所成长. 不知在何时,那就 ...

  10. Spring+SpringMVC+MyBatis+easyUI整合基础篇(一)项目简述及技术选型介绍

    作者:13GitHub:https://github.com/ZHENFENG13版权声明:本文为原创文章,未经允许不得转载. 萌芽阶段 很久之前就开始打算整理一下自己的技术博客了,由于各种原因(借口 ...

随机推荐

  1. resolv.conf

    1 这个文件由NetworkManager和network服务共同修改 关闭NetworkManager服务后,修改nameserver和hostname 重启network后,nameserver更 ...

  2. HTML 格式化等处理方法

    1.处理特殊字符串,清除空格,换行等 function DeleteHtml($str) { $str = trim ( $str ); // 清除字符串两边的空格 $str = preg_repla ...

  3. 通过JS检测360浏览器

    如何通过JS检测360浏览器? 尝试了一大堆方法,网上大多数办法都是通过navigator.userAgent来判断,这可能在几年前是行得通的,现在360userAgent输出来跟谷歌除了版本号其余一 ...

  4. Java自由块(静态和非静态)(转载)

    java中的自由块分为两种: 静态块和非静态块 静态块: public class Test { 2 static int x = 10; 3 //静态块:静态块的执行时机是在class文件装载的时候 ...

  5. [版本管理]有惊无险修复svn服务器Invalid filesystem revision number问题

    问题起因:某一天下午,团队成员在向svn服务端提交新内容,突然整栋楼断电了,自然,提交的过程被中断了.当时,还没有什么想法. 等有电后,另外一同事在update项目时,发现无法正常使用svn,一直报异 ...

  6. 理解js中__proto__和prototype的区别和关系

    首先,要明确几个点:1.在JS里,万物皆对象.方法(Function)是对象,方法的原型(Function.prototype)是对象.因此,它们都会具有对象共有的特点.即:对象具有属性__proto ...

  7. 跨域请求ajax jsonp的使用解惑

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  8. C++小项目:directx11图形程序(八):particleSysclass

    粒子系统类,粒子系统是游戏里细小元素的控制系统,虽然感觉上它对游戏的影响不大,但是其实有了它能给游戏增色不少.粒子系统控制着细小元素的生死,运动,纹理.对它的编写让我知道,游戏里的这一片从天空飘落的雪 ...

  9. C++ 类模板的使用

    从事C++挺久了,在前段时看书时,发现高手,都是在写模板无,泛型编程,顿感差距.自己连模板都没有写,于是就小小的研究了下模板的用法. 模板简而言之就是对某此对象的相同方法,或处理方式,进行归纳,总结, ...

  10. HTML5资料

    1 Canvas教程 <canvas>是一个新的用于通过脚本(通常是JavaScript)绘图的HTML元素.例如,他可以用于绘图.制作图片的组合或者简单的动画(当然并不那么简单).It ...