解決spring多執行緒不共享事務的問題
阿新 • • 發佈:2018-12-19
在一個事務中使用多執行緒操作資料庫時,若同時存在對資料庫的讀寫操作,可能出現數據讀取的不準確,因為多執行緒將不會共享同一個事務(也就是說子執行緒和主執行緒的事務不一樣),為了解決這個問題,可以使用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(); } }