1. 程式人生 > >解決spring多執行緒不共享事務的問題

解決spring多執行緒不共享事務的問題

在一個事務中使用多執行緒操作資料庫時,若同時存在對資料庫的讀寫操作,可能出現數據讀取的不準確,因為多執行緒將不會共享同一個事務(也就是說子執行緒和主執行緒的事務不一樣),為了解決這個問題,可以使用spring的分散式事務jta,並重寫JtaTransactionManager的doCommit和doRollback方法。

1、引入maven依賴

<dependency>
            <groupId>com.atomikos</groupId>
            <artifactId>transactions-jdbc</artifactId>
            <version>3.9.3</version>
        </dependency>   
        <dependency>
            <groupId>javax.transaction</groupId>
            <artifactId>jta</artifactId>
            <version>1.1</version>
        </dependency> 

2、配置xml檔案

<!-- atomikos事務管理器 -->
    <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
        init-method="init" destroy-method="close">
        <description>UserTransactionManager</description>
        <property name="forceShutdown">
            <value>true
</value> </property> </bean> <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp"> <property name="transactionTimeout" value="3000" /> </bean> <!-- spring 事務管理器,必須使用二次開發的類,控制solr的回滾 --> <bean id="springTransactionManager" class
="com.yzh.core.inner.impl.SepJtaTransactionManager"> <property name="transactionManager"> <ref bean="atomikosTransactionManager" /> </property> <property name="userTransaction"> <ref bean="atomikosUserTransaction" /> </property> <!-- 必須設定,否則程式出現異常 JtaTransactionManager does not support custom isolation levels by default --> <property name="allowCustomIsolationLevels" value="true"/> </bean> <!-- 開啟註解事務定義,由Spring掃描註解定義的事務 --> <tx:annotation-driven transaction-manager="springTransactionManager" proxy-target-class="true" />

3、將一些查詢資料庫的操作放到容器裡面,在事務提交的時候執行

public class ContextKeeper {
private static final ThreadLocal<Collection<Runnable>> keepAfterSubmit = new ThreadLocal<>();


  //將需要執行的多執行緒放到容器中
public static void put(Collection<Runnable> tasks) { keepAfterSubmit.set(tasks); }
//獲取並移除容器中的任務 public static Collection<Runnable> getAndRemoveSubmitTask() { synchronized (keepAfterSubmit) { Collection<Runnable> tasks = keepAfterSubmit.get(); keepAfterSubmit.remove(); return tasks; } }
  //移除容器中的任務
public static void removeSubmitTask() { synchronized (keepAfterSubmit) { keepAfterSubmit.remove(); } }

 

4、重寫JtaTransactionManager的doCommit和doRollback方法。

public class SepJtaTransactionManager extends JtaTransactionManager {

    /**
     * 
     */
    private static final long serialVersionUID = -1468472287996669189L;
    private static final Logger LOGGER = LoggerDeputyUtil.getSelfClassLogger();

    @Override
    protected void doCommit(DefaultTransactionStatus status) {
        super.doCommit(status);// 執行後續任務
        Collection<Runnable> tasks = ContextKeeper.getAndRemoveSubmitTask();
        if (!JudgeUtil.isEmpty(tasks)) {
            LOGGER.info("執行後續任務");
            try {
                ExecutorService pool = CommonHelper.pool();
                for (Runnable task : tasks) {
                    pool.submit(task);
                }
            } catch (Exception e) {
                ErrorLevel.LOG_HANDLERHIS_FAIL.doLog("執行後續任務失敗", e);
            }
        }
    }

    @Override
    protected void doRollback(DefaultTransactionStatus status) {
        super.doRollback(status);// 清空任務
        ContextKeeper.removeSubmitTask();
    }
}