(spring-第20回【AOP基础篇】)Spring与事务
要想了解Spring的事务,首先要了解数据库事务的基本知识,数据库并发会产生很多问题,Spring使用ThreadLocal技术来处理这些问题,那么我们必须了解Java的ThreadLocal技术。下面我们逐一了解。
第一回合:数据库事务的基本知识
什么是数据库事务?
一次执行多个SQL语句,全部执行成功则成功,有一个执行失败则全部失败。即“一荣俱荣,一损俱损”。
数据库的事务必须同时满足下列四个条件:
l 原子性(Atomic):比如数据库一次执行四个SQL语句,那么这四个SQL就是宏观的一个不可分割单元,“一荣俱荣,一损俱损”。全部执行成功则成功,有一个执行失败则全部失败。三分归元气。
l 一致性(Consistency):整个事务不管成功了还是失败了,整个数据库的状态和规则不能变化。即:A账户转账100元到B账户,这个事务过程结束后前后,数据库中总的账户金额是不变的。
l 隔离性(Isolation):不同的事务并发执行时,各自拥有不同的数据空间,你走你的阳关道,我走我的独木桥,互不干扰。
但并非完全不干扰,数据库规定了事务隔离级别,隔离级别越高,数据的一致性越好,并发性越弱。
l 持久性(Durability):一旦事务提交成功,事务中的所有数据操作都必须被持久化到数据库中。
这样一来,即使刚提交完,数据库就崩溃,当重启数据库之后,也可以根据已经保存(持久化)的操作来恢复数据。
一致性是结果,其他三个是手段。
- 数据库管理一般采用重执行日志保证原子性、一致性和持久性。
- 重执行日志记录了数据库变化的每一个动作。这样,即使数据库事务在执行了一部分操作后发生错误退出,可以根据重执行日志来撤销已经执行的操作。
- 对于已经提交的事务,即使数据库崩溃,再重启数据库时也能够根据日志对尚未持久化的数据进行相应的重执行操作。
- 数据库管理系统采用数据库锁机制来保证事务的隔离性(正如Java采用对象锁机制进行线程同步。)
数据并发的问题
多个客户端同时操作一个数据库,该并发过程就可能引起并发问题:
l 脏读(dirty read):A事务读取B事务尚未提交的数据并进行一系列操作,结果B事务执行了回滚,那么这时,A事务读到的数据就是不被认可的,是脏数据。
l 不可重复读(unrepeatable read):比如:A开始了查询事务,B开始了提款事务。A第一次查询余额为1000元,这时B提取100元,A第二次查询余额时,变成了900元,与第一次查询的余额不同。
l 幻象读(phantom read):一般发生在计算统计数据的事务中。比如;银行正在统计所有账户的存款总额,统计出来为10000元。这时正好新增了一个账户,存款1000元。再次统计发现总额为11000元,与前一次统计不同。
不可重复读是指读到了更改的数据(一般情况下需要添加行级锁,阻止操作中的数据变化),而幻象读是指读到了新增的数据(往往需要添加表级锁,将整个表锁定)。
l 第一类丢失更新:目前账户余额1000元,A开始事务-->B开始事务-->B汇入100元,余额改为1100元-->B提交事务àA取出100元,把余额改为900元-->A撤销事务-->余额恢复为1000元(丢失更新)。
A事务撤销时,把B提交的更新数据给覆盖了。
l 第二类丢失更新: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与事务的更多相关文章
- Spring+SpringMVC+MyBatis+easyUI整合基础篇(六)maven整合SSM
写在前面的话 承接前文<Spring+SpringMVC+MyBatis+easyUI整合基础篇(五)讲一下maven>,本篇所讲述的是如何使用maven与原ssm项目整合,使得一个普 ...
- Spring+SpringMVC+MyBatis+easyUI整合基础篇(八)mysql中文查询bug修复
写在前面的话 在测试搜索时出现的问题,mysql通过中文查询条件搜索不出数据,但是英文和数字可以搜索到记录,中文无返回记录.本文就是写一下发现问题的过程及解决方法.此bug在第一个项目中点这里还存在, ...
- Spring+SpringMVC+MyBatis+easyUI整合基础篇(十一)SVN服务器进阶
日常啰嗦 上一篇文章<Spring+SpringMVC+MyBatis+easyUI整合基础篇(十)SVN搭建>简单的讲了一下SVN服务器的搭建,并没有详细的介绍配置文件及一些复杂的功能, ...
- Spring+SpringMVC+MyBatis+easyUI整合基础篇(十二)阶段总结
不知不觉,已经到了基础篇的收尾阶段了,看着前面的十几篇文章,真的有点不敢相信,自己竟然真的坚持了下来,虽然过程中也有过懒散和焦虑,不过结果还是自己所希望的,克服了很多的问题,将自己的作品展现出来,也发 ...
- Spring+SpringMVC+MyBatis+easyUI整合基础篇
基础篇 Spring+SpringMVC+MyBatis+easyUI整合基础篇(一)项目简介 Spring+SpringMVC+MyBatis+easyUI整合基础篇(二)牛刀小试 Spring+S ...
- Spring cloud系列教程第十篇- Spring cloud整合Eureka总结篇
Spring cloud系列教程第十篇- Spring cloud整合Eureka总结篇 本文主要内容: 1:spring cloud整合Eureka总结 本文是由凯哥(凯哥Java:kagejava ...
- Spring基础篇——Spring的AOP切面编程
一 基本理解 AOP,面向切面编程,作为Spring的核心思想之一,度娘上有太多的教程啊.解释啊,但博主还是要自己按照自己的思路和理解再来阐释一下.原因很简单,别人的思想终究是别人的,自己的理解才是 ...
- Spring+SpringMVC+MyBatis+easyUI整合基础篇(一)项目简介
很久之前就打算开始写一下自己的技术博客了,实在抽不出时间所以计划一直搁置了,最近项目进度渐渐缓了下来,不那么忙了,也因此开始筹备自己的博客.说到这次博客的主角,也是无心插柳找到的,来源于两年前自己写的 ...
- 【SSM之旅】Spring+SpringMVC+MyBatis+Bootstrap整合基础篇(一)项目简介及技术选型相关介绍
试水 一直想去搭建个自己的个人博客,苦于自己的技术有限,然后也个人也比较懒散.想动而不能动,想动而懒得动,就这么一直拖到了现在.总觉得应该把这几年来的所学总结一番,这样才能有所成长. 不知在何时,那就 ...
- Spring+SpringMVC+MyBatis+easyUI整合基础篇(一)项目简述及技术选型介绍
作者:13GitHub:https://github.com/ZHENFENG13版权声明:本文为原创文章,未经允许不得转载. 萌芽阶段 很久之前就开始打算整理一下自己的技术博客了,由于各种原因(借口 ...
随机推荐
- CentOS6.3修复模式/单用户模式修改fstab文件
今天修改LVM逻辑卷的名称时候,忘记更改fstab配置文件了,导致机器重启后找不到盘,进不了系统!立即用光盘进入修复模式进行修复! 1.修复模式操作方法: 用光盘进入Linux修复模式,插入cent ...
- Leetcode--Generate Parentheses
主要考察栈的理解 static vector<string> generateParenthesis(int n) { vector<string> res; addingpa ...
- Linux启动盘-ultraiso
感觉windos和linux安装系统的启动盘不一样 其实我也不太懂. windos的时候我是用老毛桃 然后安装linux我就选择了ultraios作为启动盘 一: 先在百度下载 ultraios 二 ...
- angular-ui-router中的$stateProvider设置
$stateProvider .state('contacts.list', { url: '', templateUrl: 'contacts.list.html' }) .state('conta ...
- 卸载mysql
如果你的电脑里装过MySQL,想再重新安装MySQL的时候可能就会因为前一版本卸载不彻底而出现错误.最常见的就是安装好后设置参数的最后一步验证时,会在Execute configurattion步骤中 ...
- Hbase关于Java常用API举例
1. HBase相关对Admin操作的的API封装在HBaseAdmin中,封装了HBase常用操作的API 使用方法: pom.xml <!-- https://mvnrepository.c ...
- MySQL之远程登录配置
1.注释掉mysql配置文件中的这一行:#bind-address = 127.0.0.1 2.给指定服务器的用户授权:GRANT ALL PRIVILEGES ON *.* TO root@&qu ...
- PHP xml 转换为 array
retrun json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), tru ...
- js面向对象的封装方法,【案例】
封装方法: /** * @矩形canvas库 * @authors Shimily (275766400@qq.com) * @date 2016-12-28 10:30:51 * @version ...
- HttpHandler
HttpHandler初探 一.HttpHandler简介 HttpHandler是asp.net真正处理Http请求的地方.在这个HttpHandler容器中,ASP.NET Framework才真 ...