Spring 多線程事務控制的實踐
在Java多線程事務控制中,有一些注意事項和實例可以幫助你更好地理解和應用。
注意事項
- 確保線程安全:在多線程環(huán)境下,確保代碼是線程安全的。這可以通過使用synchronized關鍵字、Lock接口或Atomic類來實現(xiàn)。
- 事務的隔離級別:根據(jù)需要選擇適當?shù)氖聞崭綦x級別,以避免并發(fā)問題,例如臟讀、不可重復讀和幻讀。
- 事務的傳播行為:了解事務的傳播行為,例如事務的提交和回滾如何影響其他事務。
- 異常處理:在多線程環(huán)境下處理異常時,需要特別小心。確保捕獲和處理所有異常,并正確地處理事務回滾。
spring事務隔離級別
Java Spring框架提供了一種方便的方式來管理數(shù)據(jù)庫事務,它支持多種事務隔離級別。事務隔離級別決定了事務在并發(fā)執(zhí)行時的隔離程度,包括對其他事務的可見性和可能出現(xiàn)的并發(fā)問題。
以下是Spring框架支持的事務隔離級別及其詳細說明:
ISOLATION_DEFAULT
(默認):這是系統(tǒng)的默認隔離級別。根據(jù)具體數(shù)據(jù)庫來定義,大多數(shù)數(shù)據(jù)庫默認級別是可重復讀。ISOLATION_READ_UNCOMMITTED
(讀未提交):在這個級別,一個事務可以看到其他未提交事務的變動。這種級別可以導致臟讀、不可重復讀和幻讀的問題。ISOLATION_READ_COMMITTED
(讀提交):在這個級別,一個事務只能看到其他事務已經(jīng)提交的變動。這種級別可以避免臟讀問題,但可能會出現(xiàn)不可重復讀和幻讀的問題。ISOLATION_REPEATABLE_READ
(可重復讀):在這個級別,同一事務中多次讀取的數(shù)據(jù)是一致的。這種級別可以避免臟讀和不可重復讀的問題,但可能會出現(xiàn)幻讀的問題。ISOLATION_SERIALIZABLE
(可串行化):這是最高的事務隔離級別。在這個級別,事務串行化順序執(zhí)行,可以避免臟讀、不可重復讀和幻讀的問題。但是這種隔離級別效率低下,因為事務通常需要等待前一個事務完成,才能繼續(xù)執(zhí)行。
Spring事務的默認隔離級別與數(shù)據(jù)庫一致
在Spring中,可以通過以下方式設置事務的隔離級別:
@Transactional(isolation = Isolation.READ_COMMITTED) public void someMethod() { // some code }
在上述代碼中,@Transactional
注解指定了事務的隔離級別為READ_COMMITTED
。注意,雖然可以使用其他隔離級別,但并不是所有數(shù)據(jù)庫都支持所有的隔離級別。使用哪種隔離級別取決于你的具體需求和數(shù)據(jù)庫的能力。
spring事務傳播行為
Java Spring框架中的事務傳播行為是指在一個事務方法被另一個事務方法調(diào)用時,如何處理事務的傳播。事務傳播行為定義了在一個方法中調(diào)用另一個方法時,事務應該如何啟動、提交或回滾。
以下是Spring框架支持的事務傳播行為及其詳細說明:
PROPAGATION_REQUIRED
(必需):如果當前存在一個事務,那么就加入這個事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。PROPAGATION_SUPPORTS
(支持):支持當前事務,如果當前沒有事務,就以非事務方式執(zhí)行。PROPAGATION_MANDATORY
(強制):使用當前的事務,如果當前沒有事務,就拋出異常。PROPAGATION_REQUIRES_NEW
(新建):新建事務,如果當前存在事務,把當前事務掛起。PROPAGATION_NOT_SUPPORTED
(不支持):以非事務方式執(zhí)行操作,如果當前存在事務,就把當前事務掛起。PROPAGATION_NEVER
(從不):以非事務方式執(zhí)行,如果當前存在事務,拋出異常。PROPAGATION_NESTED
(嵌套):如果當前存在一個事務,則在嵌套事務內(nèi)執(zhí)行。如果當前沒有事務,則進行與PROPAGATION_REQUIRED類似的操作。
在Spring中,可以通過以下方式設置事務的傳播行為:
@Transactional(propagation = Propagation.REQUIRES_NEW) public void someMethod() { // some code }
在上述代碼中,@Transactional
注解指定了事務的傳播行為為REQUIRES_NEW
。根據(jù)具體情況選擇不同的事務傳播行為以確保應用程序的數(shù)據(jù)一致性和可靠性。
spring事務默認傳播行為
Spring的事務傳播行為默認是PROPAGATION_REQUIRED
,也就是如果當前存在一個事務,就加入該事務;如果當前沒有事務,就新建一個事務。
mysql事務默認隔離級別
MySQL的事務隔離級別默認是可重復讀(REPEATABLE READ
),這是大多數(shù)數(shù)據(jù)庫系統(tǒng)的默認設置。這個隔離級別可以避免臟讀和不可重復讀的問題,但可能會出現(xiàn)幻讀的問題。
多線程事務控制
Spring實現(xiàn)事務通過ThreadLocal把事務和當前線程進行了綁定。
ThreadLocal作為本地線程變量載體,保存了當前線程的變量,并確保所有變量是線程安全的。
這些封閉隔離的變量中就包含了數(shù)據(jù)庫連接,Session管理的對象以及當前事務運行的其他必要信息,而開啟的新線程是獲取不到這些變量和對象的。
也就是說:主線程事務與子線程事務是相互獨立的
怎么驗證?
驗證事務 以及 多線程事務控制編碼
package cn.cjf.tt; import cn.cjf.tt.dao.UserMapper; import cn.cjf.tt.po.User; import cn.cjf.tt.service.UserService; import lombok.SneakyThrows; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.DefaultTransactionDefinition; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; // 使用Spring整合Junit專用的類加載器 @RunWith(SpringJUnit4ClassRunner.class) // 加載配置文件或者配置類 @ContextConfiguration(locations = {"classpath:spring.xml"})//加載配置文件 public class UserTest { @Autowired private UserService userService; @Autowired private DataSourceTransactionManager transactionManager; @Autowired private UserMapper userMapper; /** * 驗證數(shù)據(jù)庫連接是否正常 */ @Test public void selectAllUser() { List<User> users = userService.selectAllUser(); for (User i : users) { System.out.println(i); } } /** * 驗證:能否正常插入數(shù)據(jù) */ public void test() { final User user = new User() {{ this.setUsername("test_" + UUID.randomUUID().toString()); this.setPassword("123456"); this.setCreateTime(new Date()); }}; userService.addUser(user); } /** * 驗證:線程池子線程中能否正常插入數(shù)據(jù) */ @Test public void testForBatch() throws InterruptedException { final ExecutorService service = Executors.newFixedThreadPool(5); for (int i = 0; i < 5; i++) { service.submit(new Runnable() { @SneakyThrows @Override public void run() { test1(); } }); } Thread.sleep(5000); } /** * 驗證:正常插入數(shù)據(jù),拋出異常后,注解事務是否回滾 */ @Transactional(rollbackFor = Exception.class) @Test public void test1() throws Exception { final User user = new User() {{ this.setUsername("test_" + UUID.randomUUID().toString()); this.setPassword("123456"); this.setCreateTime(new Date()); }}; userService.addUser(user); if (true) { throw new Exception(); } } /** * 驗證:正常插入數(shù)據(jù),拋出異常后,手動事務是否回滾 */ @Test public void test11() { DefaultTransactionDefinition dd = new DefaultTransactionDefinition(); final TransactionStatus transaction = transactionManager.getTransaction(dd); try { final User user = new User() {{ this.setUsername("test_" + UUID.randomUUID().toString()); this.setPassword("123456"); this.setCreateTime(new Date()); }}; userService.addUser(user); System.out.println(user); if (true) { throw new Exception(); } // User(id=13, username=test1694675733277, password=123456, salt=null, token=null, isEnabled=null, createTime=Thu Sep 14 15:15:33 CST 2023, modifiedTime=null) transactionManager.commit(transaction); System.out.println("---------------------" + Thread.currentThread().getName() + "事務提交"); } catch (Exception e) { e.printStackTrace(); transactionManager.rollback(transaction); System.out.println("---------------------" + Thread.currentThread().getName() + "事務回滾"); } } /** * 驗證:主線程事務,是否能影響到子線程事務 */ @Transactional(rollbackFor = Exception.class) @Test public void test2() throws Exception { final User user = new User() {{ this.setUsername("test_" + UUID.randomUUID().toString()); this.setPassword("123456"); this.setCreateTime(new Date()); }}; userService.addUser(user); System.out.println("---------------------" + Thread.currentThread().getName() + ":" + user); final ExecutorService service = Executors.newFixedThreadPool(5); List<Integer> idList = new ArrayList<>(); idList.add(user.getId()); for (int i = 0; i < 5; i++) { service.submit(new Runnable() { @Override public void run() { final User user = new User() {{ this.setUsername("test_" + UUID.randomUUID().toString()); this.setPassword("123456"); this.setCreateTime(new Date()); }}; userService.addUserForTransaction(user); System.out.println("---------------------" + Thread.currentThread().getName() + ":" + user); final Integer id = user.getId(); idList.add(id); } }); } Thread.sleep(5000); try { throw new Exception(); } finally { for (int i = 0; i < idList.size(); i++) { final Integer id = idList.get(i); final User po = userMapper.selectByPrimaryKey(id); if (po == null) { System.out.println("---------------------id:" + id + "事務回滾"); } else { System.out.println("---------------------id:" + id + "事務提交"); } // 主線程事務未結(jié)束 // 實際主線程事務回滾了,但子線程事務未回滾 } } } /** * 驗證:主線程事務,未能影響到子線程事務,是因為子線程的事務傳播行為影響 */ @Transactional(rollbackFor = Exception.class) @Test public void test21() throws Exception { final User user = new User() {{ this.setUsername("test_" + UUID.randomUUID().toString()); this.setPassword("123456"); this.setCreateTime(new Date()); }}; userService.addUser(user); System.out.println("---------------------" + Thread.currentThread().getName() + ":" + user); final ExecutorService service = Executors.newFixedThreadPool(5); List<Integer> idList = new ArrayList<>(); idList.add(user.getId()); for (int i = 0; i < 5; i++) { service.submit(new Runnable() { @Override public void run() { final User user = new User() {{ this.setUsername("test_" + UUID.randomUUID().toString()); this.setPassword("123456"); this.setCreateTime(new Date()); }}; userService.addUserForNestedTransaction(user); System.out.println("---------------------" + Thread.currentThread().getName() + ":" + user); final Integer id = user.getId(); idList.add(id); } }); } Thread.sleep(5000); try { throw new Exception(); } finally { for (int i = 0; i < idList.size(); i++) { final Integer id = idList.get(i); final User po = userMapper.selectByPrimaryKey(id); if (po == null) { System.out.println("---------------------id:" + id + "事務回滾"); } else { System.out.println("---------------------id:" + id + "事務提交"); } // 主線程事務未結(jié)束 // 實際主線程事務回滾了,但子線程事務未回滾 } } } /** * 驗證:主線程事務,未能影響到子線程事務 * 主線程手動控制事務,與注解自動控制事務,結(jié)果是否依然是,主線程事務不能影響到子線程事務 */ @Test public void test22() throws Exception { DefaultTransactionDefinition dd = new DefaultTransactionDefinition(); final TransactionStatus transaction = transactionManager.getTransaction(dd); List<Integer> idList = new ArrayList<>(); try { final User user = new User() {{ this.setUsername("test_" + UUID.randomUUID().toString()); this.setPassword("123456"); this.setCreateTime(new Date()); }}; userService.addUser(user); System.out.println("---------------------" + Thread.currentThread().getName() + ":" + user); final ExecutorService service = Executors.newFixedThreadPool(5); idList.add(user.getId()); for (int i = 0; i < 5; i++) { service.submit(new Runnable() { @Override public void run() { final User user = new User() {{ this.setUsername("test_" + UUID.randomUUID().toString()); this.setPassword("123456"); this.setCreateTime(new Date()); }}; userService.addUserForNestedTransaction(user); System.out.println("---------------------" + Thread.currentThread().getName() + ":" + user); final Integer id = user.getId(); idList.add(id); } }); } Thread.sleep(5000); if (true) { throw new Exception(); } // User(id=13, username=test1694675733277, password=123456, salt=null, token=null, isEnabled=null, createTime=Thu Sep 14 15:15:33 CST 2023, modifiedTime=null) System.out.println("---------------------" + Thread.currentThread().getName() + "事務提交"); } catch (Exception e) { e.printStackTrace(); transactionManager.rollback(transaction); System.out.println("---------------------" + Thread.currentThread().getName() + "事務回滾"); } for (int i = 0; i < idList.size(); i++) { final Integer id = idList.get(i); final User po = userMapper.selectByPrimaryKey(id); if (po == null) { System.out.println("---------------------id:" + id + "事務回滾"); } else { System.out.println("---------------------id:" + id + "事務提交"); } // 主線程事務已結(jié)束 // 主線程事務回滾了,但子線程事務未回滾 } } /** * 驗證:主線程事務,未能影響到子線程事務 * 主線程手動控制事務,子線程也手動控制事務,結(jié)果是否依然是,主線程事務不能影響到子線程事務 */ @Test public void test23() throws InterruptedException { DefaultTransactionDefinition dd = new DefaultTransactionDefinition(); final TransactionStatus transaction = transactionManager.getTransaction(dd); List<Integer> idList = new ArrayList<>(); try { final User user = new User() {{ this.setUsername("test_" + UUID.randomUUID().toString()); this.setPassword("123456"); this.setCreateTime(new Date()); }}; userService.addUser(user); idList.add(user.getId()); final ExecutorService service = Executors.newFixedThreadPool(5); for (int i = 0; i < 5; i++) { service.submit(new Runnable() { @Override public void run() { DefaultTransactionDefinition dd = new DefaultTransactionDefinition(); final TransactionStatus transaction = transactionManager.getTransaction(dd); try { final User user = new User() {{ this.setUsername("test_" + UUID.randomUUID().toString()); this.setPassword("123456"); this.setCreateTime(new Date()); }}; userService.addUser(user); System.out.println(user); final Integer id = user.getId(); idList.add(id); // User(id=13, username=test1694675733277, password=123456, salt=null, token=null, isEnabled=null, createTime=Thu Sep 14 15:15:33 CST 2023, modifiedTime=null) transactionManager.commit(transaction); System.out.println("---------------------" + Thread.currentThread().getName() + "事務提交"); } catch (Exception e) { e.printStackTrace(); transactionManager.rollback(transaction); System.out.println("---------------------" + Thread.currentThread().getName() + "事務回滾"); } } }); } Thread.sleep(5000); if (true) { throw new Exception(); } // User(id=13, username=test1694675733277, password=123456, salt=null, token=null, isEnabled=null, createTime=Thu Sep 14 15:15:33 CST 2023, modifiedTime=null) System.out.println("---------------------" + Thread.currentThread().getName() + "事務提交"); } catch (Exception e) { e.printStackTrace(); transactionManager.rollback(transaction); System.out.println("---------------------" + Thread.currentThread().getName() + "事務回滾"); } for (int i = 0; i < idList.size(); i++) { final Integer id = idList.get(i); final User po = userMapper.selectByPrimaryKey(id); if (po == null) { System.out.println("---------------------id:" + id + "事務回滾"); } else { System.out.println("---------------------id:" + id + "事務提交"); } // 主線程事務已結(jié)束 // 主線程事務回滾了,但子線程事務未回滾 } } /** * 驗證結(jié)果:主線程事務不能影響到子線程事務 * <p> * 主線程,子線程控制各自事務,等待一起提交 */ @Test public void test3() throws InterruptedException { DefaultTransactionDefinition dd = new DefaultTransactionDefinition(); final TransactionStatus transaction = transactionManager.getTransaction(dd); List<Integer> idList = new ArrayList<>(); int time = 5; CountDownLatch cdl = new CountDownLatch(time); AtomicBoolean flag = new AtomicBoolean(true); try { final User user = new User() {{ this.setUsername("test_" + UUID.randomUUID().toString()); this.setPassword("123456"); this.setCreateTime(new Date()); }}; userService.addUser(user); idList.add(user.getId()); final ExecutorService service = Executors.newFixedThreadPool(time); for (int i = 0; i < time; i++) { service.submit(new Runnable() { @SneakyThrows @Override public void run() { DefaultTransactionDefinition dd = new DefaultTransactionDefinition(); final TransactionStatus transaction = transactionManager.getTransaction(dd); try { final User user = new User() {{ this.setUsername("test_" + UUID.randomUUID().toString()); this.setPassword("123456"); this.setCreateTime(new Date()); }}; userMapper.insertSelective(user); idList.add(user.getId()); System.out.println("---------------" + Thread.currentThread().getName() + "--執(zhí)行成功"); } catch (Exception e) { e.printStackTrace(); System.out.println("---------------" + Thread.currentThread().getName() + "--執(zhí)行失敗,等待事務回滾"); flag.set(false); } finally { System.out.println("---------------" + Thread.currentThread().getName() + "--等待"); cdl.countDown(); cdl.await(); if (flag.get()) { System.out.println("---------------" + Thread.currentThread().getName() + "--提交事務"); transactionManager.commit(transaction); } else { System.out.println("---------------" + Thread.currentThread().getName() + "--回滾事務"); transactionManager.rollback(transaction); } System.out.println("---------------" + Thread.currentThread().getName() + "--End"); } } }); } } catch (Exception e) { e.printStackTrace(); System.out.println("---------------" + Thread.currentThread().getName() + "--執(zhí)行失敗,等待事務回滾"); flag.set(false); } finally { System.out.println("---------------" + Thread.currentThread().getName() + "--等待"); cdl.await(); if (flag.get()) { System.out.println("---------------" + Thread.currentThread().getName() + "--提交事務"); transactionManager.commit(transaction); } else { System.out.println("---------------" + Thread.currentThread().getName() + "--回滾事務"); transactionManager.rollback(transaction); } } System.out.println("---------------" + Thread.currentThread().getName() + "--End"); for (int i = 0; i < idList.size(); i++) { final Integer id = idList.get(i); final User po = userMapper.selectByPrimaryKey(id); if (po == null) { System.out.println("---------------------id:" + id + "事務回滾"); } else { System.out.println("---------------------id:" + id + "事務提交"); } // 主線程事務已結(jié)束 // 主線程事務回滾了,子線程事務也回滾 } } /** * 驗證結(jié)果:主線程事務不能影響到子線程事務 * <p> * 主線程,子線程控制各自事務,等待一起提交 * 驗證,主線程異常,子線程未異常,事務都回滾了 */ @Test public void test31() throws InterruptedException { DefaultTransactionDefinition dd = new DefaultTransactionDefinition(); final TransactionStatus transaction = transactionManager.getTransaction(dd); List<Integer> idList = new ArrayList<>(); int time = 5; CountDownLatch cdl = new CountDownLatch(time); AtomicBoolean flag = new AtomicBoolean(true); try { final User user = new User() {{ this.setUsername("test_" + UUID.randomUUID().toString()); this.setPassword("123456"); this.setCreateTime(new Date()); }}; userService.addUser(user); idList.add(user.getId()); final ExecutorService service = Executors.newFixedThreadPool(time); for (int i = 0; i < time; i++) { // int finalI = i; service.submit(new Runnable() { @SneakyThrows @Override public void run() { DefaultTransactionDefinition dd = new DefaultTransactionDefinition(); final TransactionStatus transaction = transactionManager.getTransaction(dd); try { final User user = new User() {{ this.setUsername("test_" + UUID.randomUUID().toString()); this.setPassword("123456"); this.setCreateTime(new Date()); }}; userMapper.insertSelective(user); idList.add(user.getId()); // 最后一個提交的任務,拋出異常;注釋掉會全部完成,否則全部回滾 // if (finalI == time - 1) { // throw new RuntimeException(); // } // User(id=13, username=test1694675733277, password=123456, salt=null, token=null, isEnabled=null, createTime=Thu Sep 14 15:15:33 CST 2023, modifiedTime=null) System.out.println("---------------" + Thread.currentThread().getName() + "--執(zhí)行成功"); } catch (Exception e) { e.printStackTrace(); System.out.println("---------------" + Thread.currentThread().getName() + "--執(zhí)行失敗,等待事務回滾"); flag.set(false); } finally { System.out.println("---------------" + Thread.currentThread().getName() + "--等待"); cdl.countDown(); cdl.await(); if (flag.get()) { System.out.println("---------------" + Thread.currentThread().getName() + "--提交事務"); transactionManager.commit(transaction); } else { System.out.println("---------------" + Thread.currentThread().getName() + "--回滾事務"); transactionManager.rollback(transaction); } System.out.println("---------------" + Thread.currentThread().getName() + "--End"); } } }); } if (true) { throw new Exception(); } } catch (Exception e) { e.printStackTrace(); System.out.println("---------------" + Thread.currentThread().getName() + "--執(zhí)行失敗,等待事務回滾"); flag.set(false); } finally { System.out.println("---------------" + Thread.currentThread().getName() + "--等待"); cdl.await(); if (flag.get()) { System.out.println("---------------" + Thread.currentThread().getName() + "--提交事務"); transactionManager.commit(transaction); } else { System.out.println("---------------" + Thread.currentThread().getName() + "--回滾事務"); transactionManager.rollback(transaction); } } System.out.println("---------------" + Thread.currentThread().getName() + "--End"); for (int i = 0; i < idList.size(); i++) { final Integer id = idList.get(i); final User po = userMapper.selectByPrimaryKey(id); if (po == null) { System.out.println("---------------------id:" + id + "事務回滾"); } else { System.out.println("---------------------id:" + id + "事務提交"); } // 主線程事務已結(jié)束 // 主線程事務回滾了,子線程事務也回滾 } } /** * 驗證結(jié)果:主線程事務不能影響到子線程事務 * <p> * 主線程,子線程控制各自事務,等待一起提交 * 驗證,主線程未異常,子線程異常,事務都回滾了 */ @Test public void test32() throws InterruptedException { DefaultTransactionDefinition dd = new DefaultTransactionDefinition(); final TransactionStatus transaction = transactionManager.getTransaction(dd); List<Integer> idList = new ArrayList<>(); int time = 5; CountDownLatch cdl = new CountDownLatch(time); AtomicBoolean flag = new AtomicBoolean(true); try { final User user = new User() {{ this.setUsername("test_" + UUID.randomUUID().toString()); this.setPassword("123456"); this.setCreateTime(new Date()); }}; userService.addUser(user); idList.add(user.getId()); final ExecutorService service = Executors.newFixedThreadPool(time); for (int i = 0; i < time; i++) { int finalI = i; service.submit(new Runnable() { @SneakyThrows @Override public void run() { DefaultTransactionDefinition dd = new DefaultTransactionDefinition(); final TransactionStatus transaction = transactionManager.getTransaction(dd); try { final User user = new User() {{ this.setUsername("test_" + UUID.randomUUID().toString()); this.setPassword("123456"); this.setCreateTime(new Date()); }}; userMapper.insertSelective(user); idList.add(user.getId()); // 最后一個提交的任務,拋出異常;注釋掉會全部完成,否則全部回滾 if (finalI == time - 1) { throw new RuntimeException(); } // User(id=13, username=test1694675733277, password=123456, salt=null, token=null, isEnabled=null, createTime=Thu Sep 14 15:15:33 CST 2023, modifiedTime=null) System.out.println("---------------" + Thread.currentThread().getName() + "--執(zhí)行成功"); } catch (Exception e) { e.printStackTrace(); System.out.println("---------------" + Thread.currentThread().getName() + "--執(zhí)行失敗,等待事務回滾"); flag.set(false); } finally { System.out.println("---------------" + Thread.currentThread().getName() + "--等待"); cdl.countDown(); cdl.await(); if (flag.get()) { System.out.println("---------------" + Thread.currentThread().getName() + "--提交事務"); transactionManager.commit(transaction); } else { System.out.println("---------------" + Thread.currentThread().getName() + "--回滾事務"); transactionManager.rollback(transaction); } System.out.println("---------------" + Thread.currentThread().getName() + "--End"); } } }); } // if (true) { // throw new Exception(); // } } catch (Exception e) { e.printStackTrace(); System.out.println("---------------" + Thread.currentThread().getName() + "--執(zhí)行失敗,等待事務回滾"); flag.set(false); } finally { System.out.println("---------------" + Thread.currentThread().getName() + "--等待"); cdl.await(); if (flag.get()) { System.out.println("---------------" + Thread.currentThread().getName() + "--提交事務"); transactionManager.commit(transaction); } else { System.out.println("---------------" + Thread.currentThread().getName() + "--回滾事務"); transactionManager.rollback(transaction); } } System.out.println("---------------" + Thread.currentThread().getName() + "--End"); for (int i = 0; i < idList.size(); i++) { final Integer id = idList.get(i); final User po = userMapper.selectByPrimaryKey(id); if (po == null) { System.out.println("---------------------id:" + id + "事務回滾"); } else { System.out.println("---------------------id:" + id + "事務提交"); } // 主線程事務已結(jié)束 // 主線程事務回滾了,子線程事務也回滾 } } }
到此這篇關于Spring 多線程事務控制的實踐的文章就介紹到這了,更多相關Spring 多線程事務控制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
idea創(chuàng)建包含多個springboot module的maven project的方法
這篇文章主要介紹了idea創(chuàng)建包含多個springboot module的maven project的方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09

Java使用正則表達式截取重復出現(xiàn)的XML字符串功能示例

Java中HashMap和Hashtable的區(qū)別小結(jié)

解決lambda表達式內(nèi)出現(xiàn)異常無法throw拋出的問題

java使用google身份驗證器實現(xiàn)動態(tài)口令驗證的示例

java實現(xiàn)微信掃碼登錄第三方網(wǎng)站功能(原理和代碼)