Spring的事務管理基礎知識

一、數據庫事務基礎知識java

    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(
  ...
)

 

二、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採用了「以空間換時間」的方式:訪問並行化,對象獨享化。前者僅提供一份變量,讓不一樣的線程排隊訪問,然後者爲每個線程都提供了一份變量,所以能夠同時訪問而互不影響。
相關文章
相關標籤/搜索