Spring的事务管理基础知识

1、数据库事务基础知识

1)数据库事务有严格的定义,它必须同时满足4个特性:原子性(Atomic)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),简称ACID。

2)数据并发的问题:脏读、不可重复读、幻想读、第一类丢失更新、第二类丢失更新。

3)数据库锁机制:

按锁定的对象的不同:一般可以分别表锁定和行锁定,前者对整个表进行锁定,而后者对表中特定行进行锁定。从并发事务锁定的关系上看,可以分为共享锁定和独占锁定。共享锁定会防止独占锁定,但允许其他的共享锁定。而独占锁定既防止其他的独占锁定,也防止其他的共享锁定。为了更改数据,数据库必须在进行更改的行上施加行独占锁定,INSERT、UPDATE、DELETE和SELECT FOR UPDATE语句都会隐式采用必要的行锁定。

4)事务隔离级别:READ UNCOMMITED 、 READ COMMITTED、REPEATABLE READ、SERIALIZABLE。

5)JDBC对事务支持

用户可以通过Connection#getMetaData()方法获取DatabaseMetaData对象,并通过该对象的supportsTransactions()、supportsTransactionIsolationLevel(int level)方法查看底层数据库的事务支持情况。Connection默认情况下是自动提交的,也即每条执行的SQL都对应一个事务,为了能够将多条SQL当成一个事务执行,必须先通过Connection#setAutoCommit(false)阻止Connection自动提交,并可通过Connection#setTransactionIsolation()设置事务的隔离级别,Connection中定义了对应SQL92标准4个事务隔离级别的常量。通过Connection#commit()提交事务,通过Connection#rollback()回滚事务。

典型的JDBC事务数据操作的代码:

Connection conn;
try{
  conn = DriverManager.getConnection();
  conn.setAutoCommit(false);
  conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
  Statement stmt = conn.createStatement();
  int rows = stmt.executeUpdate("INSERT INTO t_topic VALUES(1,‘tom‘)");
  rows = stmt.executeUpdate("UPDATE t_user set topic_nums = topic_nums + 1 WHERE user_id = 1");

  conn.commit();
}catch(Exception e){
  ...
  conn.rollback();
}finally(
  ...
)

2、ThreadLocal基础知识

ThreadLocal,顾名思义,他不是一个线程,而是线程的一个本地化对象。当工作于多线程中的对象使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程分配一个独立的变量副本。所以每一个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。从线程的角度看,这个变量就像是线程的本地变量,这也是类名中“Local”所要表达的意思。

ThreadLocal类接口很简单,只有4个方法:

1)void set(Object value):设置当前线程的线程局部变量的值;

2)public Object get():该方法返回当前线程所对应的线程局部变量;

3)public void remove():将当前线程局部变量的值删除,目的是为了减少内存的利用。

4)protected Object initialValue():返回该线程局部变量的初始值。

ThreadLocal是如何做到为每一个线程维护一份独立的变量副本呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素键为线程对象,而值对应线程的变量副本。

简单的实现版本:

package com.yyq.transaction;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class SimpleThreadLocal {
    private Map valueMap = Collections.synchronizedMap(new HashMap());
    public void set(Object newValue){
        valueMap.put(Thread.currentThread(),newValue);
    }

    public Object get(){
        Thread currentThread = Thread.currentThread();
        Object o = valueMap.get(currentThread);
        if (o == null && !valueMap.containsKey(currentThread)){
            o = initialValue();
            valueMap.put(currentThread,o);
        }
        return o;
    }

    public void remove(){
        valueMap.remove(Thread.currentThread());
    }

    public Object initialValue(){
        return null;
    }
}

一个ThreadLocal实例:

package com.yyq.transaction;
public class SequenceNumber {
    private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>() {
        public Integer initialValue() {
            return 0;
        }
    };
    public int getNextNum() {
        seqNum.set(seqNum.get() + 1);
        return seqNum.get();
    }
    private static class TestClient extends Thread {
        private SequenceNumber sn;
        public TestClient(SequenceNumber sn) {
            this.sn = sn;
        }
        public void run() {
            for (int i = 0; i < 3; i++) {
                System.out.println("thread[" + Thread.currentThread().getName() + "]sn[" + sn.getNextNum() + "]");
            }
        }
    }
    public static void main(String[] args) {
        SequenceNumber sn = new SequenceNumber();
        TestClient t1 = new TestClient(sn);
        TestClient t2 = new TestClient(sn);
        TestClient t3 = new TestClient(sn);
        t1.start();
        t2.start();
        t3.start();
    }
}

输出结果:

thread[Thread-1]sn[1]

thread[Thread-1]sn[2]

thread[Thread-1]sn[3]

thread[Thread-0]sn[1]

thread[Thread-0]sn[2]

thread[Thread-0]sn[3]

thread[Thread-2]sn[1]

thread[Thread-2]sn[2]

thread[Thread-2]sn[3]

根据输出结果可以发现每个线程所产生的序号虽然都共享同一个SequenceNumber实例,但它们并没有发生相互干扰的情况,而是各自产生独立的序列号,这是因为我们通过ThreadLocal为每一个线程提供了单独的副本。

在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序缜密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。

而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal为每一个线程提供一个独立的变量副本,从而隔离了多个线程对访问数据的冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对变量进行同步了。ThreadLocal提供了线程安全的对象封装,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。

概况起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式:访问串行化,对象共享化。而ThreadLocal采用了“以空间换时间”的方式:访问并行化,对象独享化。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

时间: 2024-11-05 18:40:18

Spring的事务管理基础知识的相关文章

[Spring框架]Spring 事务管理基础入门总结.

前言:在之前的博客中已经说过了数据库的事务, 不过那里面更多的是说明事务的一些锁机制, 今天来说一下Spring管理事务的一些基础知识. 之前的文章: [数据库事务与锁]详解一: 彻底理解数据库事务一, 什么是事务 事务是逻辑上一组操作,这组操作要么全都成功,要么全都失败. 事务的属性: ACID原子性(Atomicity): 事务作为一个整体被执行,包含在其中的对数据的操作要么全部被执行,要么都不执行.一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态.

Spring事务管理——基础会用篇

之前说到Spring的事务管理 一直很懵逼 ,只知道事务管理大概是干嘛的. 网上的博客都是用 银行转账来解释 事务管理,哈哈哈 那我也用这个吧,这个例子的确是最好的. 说是两个人相互转账,A转500块钱给B,至于A为啥转钱给B那是她们直接的事情.那么问题来了,比如A转钱给B的时候,突然断电了,A 的钱已经从数据库中扣了,但并没有执行存入到B账户的操作.如果没有事务管理,那这500快就没了  但有了事务管理 那之前的这个从A扣的钱 就会回滚 本次操作 就不成功.所以A就不会扣钱 .至于原理是什么,

深入Spring:自定义事务管理

转自: http://www.jianshu.com/p/5347a462b3a5 前言 上一篇文章讲了Spring的Aop,这里讲一下Spring的事务管理,Spring的事务管理是建立在Aop的基础上的,相比Aop,事务管理的实现耦合性比较小,自定义就比较简单了. 自定义事务 Spring的开启事务管理主要是通过@EnableTransactionManagement注解来实现的.查看源码就会发现,这个注解主要是注入了两个类InfrastructureAdvisorAutoProxyCrea

spring+mybatis事务管理

spring+mybatis事务管理 最近在和朋友做一个项目,考虑用springmvc+mybatis来做,之前在公司工作吧,对于数据库这块的配置也有人再弄,最近因为这个项目,我就上网学习了一些关于数据库配置这方面的东西吧,今天给大家分享一下关于spring+mybatis管理事务这方面得知识吧. 先说说spring管理事务的集中方式,我所了解的有两种:第一种是编程式事务管理,第二种是声明式事务管理,而声明式事务管理中又有两种,一种是配置方式,另一种是声明式.我们在工作中一般都使用声明式事务管理

Spring框架事务管理机制

一.事务的基本原理Spring事务 的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的.对于纯JDBC操作数据库,想要用到事务,可以按照以下步骤进行:    获取连接 Connection con = DriverManager.getConnection()    开启事务con.setAutoCommit(true/false);    执行CRUD    提交事务/回滚事务 con.commit() / con.rollback();    关闭连接

Spring Boot事务管理(中)

在上一篇 Spring Boot事务管理(上)的基础上介绍Spring Boot事务属性和事务回滚规则 . 4 Spring Boot事务属性 什么是事务属性呢?事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上.事务属性包含了5个方面,如图所示,它们定义于TransactionDefinition接口类   1. 事务隔离级别 隔离级别是指若干个并发事务之间的隔离程度. Spring Boot的隔离级别被封装在枚举类Isolation,枚举值取自接口TransactionDe

Spring Boot事务管理(下)

在上两篇 Spring Boot事务管理(上)和Spring Boot事务管理(中)的基础上介绍注解@Transactional. 5 @Transactional属性 属性 类型 描述 value String 指定使用的事务管理器 propagation enum: Propagation 可选的事务传播行为设置 isolation enum: Isolation 可选的事务隔离级别设置 readOnly boolean 读写或只读事务,默认读写 timeout int,unit secon

spring与事务管理

就我接触到的事务,使用最多的事务管理器是JDBC事务管理器.现在就记录下在spring中是如何使用JDBC事务管理器 1)在spring中配置事务管理器 <!-- JDBC事务 -->    <bean id="jdbcTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <property

Spring初学之spring的事务管理xml

所有的java类都是用的上一篇文章:Spring初学之spring的事务管理 不同的是,这时xml配置事务,所以就要把java类中的那些关于spring的注解都删掉,然后在xml中配置,ApplicationContext.xml如下: 1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans&q