zoukankan      html  css  js  c++  java
  • Spring -- 5.1

    Spring事务管理的一些基础知识

    • JDBC对事务的支持
      首先要知道并不是所有的数据库都支持事务,即使支持也并非支持所有的事务隔离级别,我们可以通过Connection#getMetaData()方法获取DataBaseMetaData(数据库元数据)对象,并通过该对象的supportsTransactions()、supportsTransationIsolationLevel(int level)来查看底层数据库的事务支持情况。

      JDBC的Connection对象默认是自动提交事务的,也即每条执行的SQL都对应一个事务。现在再回头想想自己刚开始写JDBC链接数据库的时候,对事务不清楚,也没有提交事务的意识,之所以没有出错很可能就是这个自动提交事务的作用。但是如果我们要将多条sql语句封装到一个事务中,这中写法就不行了(对每一条sql创建一个事务,执行完立刻提交的默认方式),我们必须手动做一些事务上操作:首先Connection#setAutoCommit(false)阻止Connection自动提交事务,通过Conneciton#setTransactionIsolation()设置事务的隔离级别,然后通过Connection#commit()提交事务,Connection#rollback()回滚事务(撤销同一个事务中的数据库操作)。一个典型的的jdbc事务数据操作代码,附图:

      咱们再深入一点来看看这个JDBC的事务。
      在jdk2.0中,事务最终只能有两个操作:提交和回滚。在jdk1.4及以后的版本中,引入了一个全新的保存点特性,savePoint接口允许用户将事务分割为多个阶段,用户可以指定回滚到特定的保存点。


      并非所有的数据库都支持保存点功能,我们可以通过DataBaseMetaData#supportsSavepoints()方法查看是否支持。
    • ThreadLocal基础和Spring使用ThreadLocal解决数据库访问的线程安全问题

      About ThreadLocal

      java.lang.ThreadLocal是为解决多线程程序的并发问题而提供的一种新的解决思路。ThreadLocal,顾名思义,是线程的一个本地化变量。当工作于多线程中的对象使用ThreadLocal维护变量时,ThreadLocal为每一个使用该变量的线程分配一个独立的变量副本。每一个线程都可以独立的改变自己的变量副本,而不会影响其他线程的变量副本。我们从线程的角度看,这个变量就像是线程的本地变量,这也是类名中Local所要表达的意思。简而言之,同一个类,在不同的线程中,从ThreadLocal中取得自己对应的变量副本,解决多线程共享状态变量的安全性问题。
      我们考究一下ThreadLocal如何做到为每一个线程维护一份独立的变量副本呢?实现思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。
      写一个例子吧
      	//通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值。
      	private static ThreadLocal<Integer> tdLocal = new ThreadLocal<Integer>(){
      		public Integer initialValue(){
      			return 0;
      		}
      	};
      	
      	//获取下一个序列值。
      	public int getNextNum(){
      		tdLocal.set(tdLocal.get()+1);
      		return tdLocal.get();
      	}
      	
      	//私有的测试线程类。
      	private static class TestClient extends Thread{
      		private SequenceNum seqNum;
      		public TestClient(SequenceNum seqNum){
      			this.seqNum = seqNum;
      		}
      		public void run(){
      			for(int i=0;i<3;i++){
      				System.out.println("Thread is ["
      						+ Thread.currentThread().getName() + "],序列号是  "
      						+ seqNum.getNextNum());
      			}
      		}
      	}
      	
      	
      	public static void main(String[] args) {
      		SequenceNum seqNum = new SequenceNum();
      		TestClient client1 = new TestClient(seqNum);
      		TestClient client2 = new TestClient(seqNum);
      		TestClient client3 = new TestClient(seqNum);
      		TestClient client4 = new TestClient(seqNum);
      		//运行线程。
      		client1.start();
      		client2.start();
      		client3.start();
      		client4.start();
      	}
      ==================================================
      Thread is [Thread-1],序列号是  1
      Thread is [Thread-2],序列号是  1
      Thread is [Thread-0],序列号是  1
      Thread is [Thread-3],序列号是  1
      Thread is [Thread-3],序列号是  2
      Thread is [Thread-0],序列号是  2
      Thread is [Thread-2],序列号是  2
      Thread is [Thread-2],序列号是  3
      Thread is [Thread-1],序列号是  2
      Thread is [Thread-0],序列号是  3
      Thread is [Thread-3],序列号是  3
      Thread is [Thread-1],序列号是  3
      

      我们看到这四个线程共享了同一个SequenceNum实例,但是线程之间并没有相互的影响,而是各自产生独立的序列号,不存在线程安全性的问题,这是因为我们通过ThreadLocal为每一个线程提供了单独的变量副本。这也是Spring解决线程安全的机制。
      ÕÕÕÕ
      书中有一句话很生动形象的对比了同步机制和ThreadLocal解决线程安全问题不同点:对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式:访问串行化,对象共享化;而ThreadLocal采用了“以空间换时间”的方式:访问并行化,对象独立化。前者提供一份变量,让不同的线程排队访问,而后者为每一份线程提供了一份变量,因此可以同时访问而互不影响。

      Spring使用ThreadLocal解决线程安全的问题。
      我们大概都有这么一个概念,只有无状态的Bean才能在多线程环境下共享,在Spring中,绝大部分的Bean都可以声明为singleton作用域。因为Spring对一些Bean(如RequestContextHolder,TranscationSynchronizationManager,LocalContextHolder)中非线程安全的“状态性对象”采用ThreadLocal进行封装,把他们变成线程安全的“状态性对象”,因此有状态的Bean能够以Singleton的方式在多线程中正常工作了。这里小可我说一下我的具体感受:原来Spring注入的JdbcTemplate是单例模式!其实就一个模板对象,把他注入到多个Service中,原来怎么没有注意到这一点呢?还有对有状态对象的理解,这个说的直白一点就是这个变量在不同的线程中不一样,尼玛取了一个有状态对象(变量)感觉好迷糊人啊!呵呵
      看看书中对多线程的理解:
      一般的Web应用划分为展现层、服务层和持久层三个层次,在不同的层中编写不同的逻辑,下次通过接口向上层开发接口调用。在一般的情况下,从接收请求到返回相应所经过的所有程序调用都同属于一个线程(看到这句话,我就想到了Servlet和CGI的区别,一个请求到来Servlet就会新建一个线程去响应这个请求,而CGI则是需要新建一个进程,所以Servlet的效率更高。尼玛这是以前看过的一个面试题啊,终于对上了),如图:

      这样根据需要,把一些非线程安全的变量存放到ThreadLocal中,在同一次请求响应的调用线程中,所有对象访问同一ThreadLocal变量都是当前线程绑定的。
      怎么理解Spring中DAO和Service都是以单实例的bean形式存在这句话,就是不同的线程调用的都是同一个DAO和Service的实例,只有一个实例。
      http://blog.csdn.net/c289054531/article/category/1473443   

  • 相关阅读:
    html 的一些基础操作
    java 通过反射调用属性,方法,构造器
    java 通过反射获取类属性结构,类方法,类父类及其泛型,类,接口和包
    java 反射,类的加载过程以及Classloader类加载器
    java 随机读写访问流及seek方法
    java 序列化机制
    java 标准输入输出流,打印流,数据流
    hp400 硒鼓加粉图解
    Delphi XE5 android 获取网络状态
    Delphi XE5 常见问题解答
  • 原文地址:https://www.cnblogs.com/lovery/p/3745712.html
Copyright © 2011-2022 走看看