Spring 多線程事務(wù)控制的實(shí)踐
在Java多線程事務(wù)控制中,有一些注意事項(xiàng)和實(shí)例可以幫助你更好地理解和應(yīng)用。
注意事項(xiàng)
- 確保線程安全:在多線程環(huán)境下,確保代碼是線程安全的。這可以通過使用synchronized關(guān)鍵字、Lock接口或Atomic類來實(shí)現(xiàn)。
- 事務(wù)的隔離級別:根據(jù)需要選擇適當(dāng)?shù)氖聞?wù)隔離級別,以避免并發(fā)問題,例如臟讀、不可重復(fù)讀和幻讀。
- 事務(wù)的傳播行為:了解事務(wù)的傳播行為,例如事務(wù)的提交和回滾如何影響其他事務(wù)。
- 異常處理:在多線程環(huán)境下處理異常時,需要特別小心。確保捕獲和處理所有異常,并正確地處理事務(wù)回滾。
spring事務(wù)隔離級別
Java Spring框架提供了一種方便的方式來管理數(shù)據(jù)庫事務(wù),它支持多種事務(wù)隔離級別。事務(wù)隔離級別決定了事務(wù)在并發(fā)執(zhí)行時的隔離程度,包括對其他事務(wù)的可見性和可能出現(xiàn)的并發(fā)問題。
以下是Spring框架支持的事務(wù)隔離級別及其詳細(xì)說明:
ISOLATION_DEFAULT(默認(rèn)):這是系統(tǒng)的默認(rèn)隔離級別。根據(jù)具體數(shù)據(jù)庫來定義,大多數(shù)數(shù)據(jù)庫默認(rèn)級別是可重復(fù)讀。ISOLATION_READ_UNCOMMITTED(讀未提交):在這個級別,一個事務(wù)可以看到其他未提交事務(wù)的變動。這種級別可以導(dǎo)致臟讀、不可重復(fù)讀和幻讀的問題。ISOLATION_READ_COMMITTED(讀提交):在這個級別,一個事務(wù)只能看到其他事務(wù)已經(jīng)提交的變動。這種級別可以避免臟讀問題,但可能會出現(xiàn)不可重復(fù)讀和幻讀的問題。ISOLATION_REPEATABLE_READ(可重復(fù)讀):在這個級別,同一事務(wù)中多次讀取的數(shù)據(jù)是一致的。這種級別可以避免臟讀和不可重復(fù)讀的問題,但可能會出現(xiàn)幻讀的問題。ISOLATION_SERIALIZABLE(可串行化):這是最高的事務(wù)隔離級別。在這個級別,事務(wù)串行化順序執(zhí)行,可以避免臟讀、不可重復(fù)讀和幻讀的問題。但是這種隔離級別效率低下,因?yàn)槭聞?wù)通常需要等待前一個事務(wù)完成,才能繼續(xù)執(zhí)行。
Spring事務(wù)的默認(rèn)隔離級別與數(shù)據(jù)庫一致
在Spring中,可以通過以下方式設(shè)置事務(wù)的隔離級別:
@Transactional(isolation = Isolation.READ_COMMITTED)
public void someMethod() {
// some code
}在上述代碼中,@Transactional注解指定了事務(wù)的隔離級別為READ_COMMITTED。注意,雖然可以使用其他隔離級別,但并不是所有數(shù)據(jù)庫都支持所有的隔離級別。使用哪種隔離級別取決于你的具體需求和數(shù)據(jù)庫的能力。
spring事務(wù)傳播行為
Java Spring框架中的事務(wù)傳播行為是指在一個事務(wù)方法被另一個事務(wù)方法調(diào)用時,如何處理事務(wù)的傳播。事務(wù)傳播行為定義了在一個方法中調(diào)用另一個方法時,事務(wù)應(yīng)該如何啟動、提交或回滾。
以下是Spring框架支持的事務(wù)傳播行為及其詳細(xì)說明:
PROPAGATION_REQUIRED(必需):如果當(dāng)前存在一個事務(wù),那么就加入這個事務(wù),如果當(dāng)前沒有事務(wù),就新建一個事務(wù)。這是最常見的選擇。PROPAGATION_SUPPORTS(支持):支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就以非事務(wù)方式執(zhí)行。PROPAGATION_MANDATORY(強(qiáng)制):使用當(dāng)前的事務(wù),如果當(dāng)前沒有事務(wù),就拋出異常。PROPAGATION_REQUIRES_NEW(新建):新建事務(wù),如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起。PROPAGATION_NOT_SUPPORTED(不支持):以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。PROPAGATION_NEVER(從不):以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),拋出異常。PROPAGATION_NESTED(嵌套):如果當(dāng)前存在一個事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒有事務(wù),則進(jìn)行與PROPAGATION_REQUIRED類似的操作。
在Spring中,可以通過以下方式設(shè)置事務(wù)的傳播行為:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void someMethod() {
// some code
}在上述代碼中,@Transactional注解指定了事務(wù)的傳播行為為REQUIRES_NEW。根據(jù)具體情況選擇不同的事務(wù)傳播行為以確保應(yīng)用程序的數(shù)據(jù)一致性和可靠性。
spring事務(wù)默認(rèn)傳播行為
Spring的事務(wù)傳播行為默認(rèn)是PROPAGATION_REQUIRED,也就是如果當(dāng)前存在一個事務(wù),就加入該事務(wù);如果當(dāng)前沒有事務(wù),就新建一個事務(wù)。
mysql事務(wù)默認(rèn)隔離級別
MySQL的事務(wù)隔離級別默認(rèn)是可重復(fù)讀(REPEATABLE READ),這是大多數(shù)數(shù)據(jù)庫系統(tǒng)的默認(rèn)設(shè)置。這個隔離級別可以避免臟讀和不可重復(fù)讀的問題,但可能會出現(xiàn)幻讀的問題。
多線程事務(wù)控制
Spring實(shí)現(xiàn)事務(wù)通過ThreadLocal把事務(wù)和當(dāng)前線程進(jìn)行了綁定。
ThreadLocal作為本地線程變量載體,保存了當(dāng)前線程的變量,并確保所有變量是線程安全的。
這些封閉隔離的變量中就包含了數(shù)據(jù)庫連接,Session管理的對象以及當(dāng)前事務(wù)運(yùn)行的其他必要信息,而開啟的新線程是獲取不到這些變量和對象的。
也就是說:主線程事務(wù)與子線程事務(wù)是相互獨(dú)立的
怎么驗(yàn)證?
驗(yàn)證事務(wù) 以及 多線程事務(wù)控制編碼
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;
/**
* 驗(yàn)證數(shù)據(jù)庫連接是否正常
*/
@Test
public void selectAllUser() {
List<User> users = userService.selectAllUser();
for (User i : users) {
System.out.println(i);
}
}
/**
* 驗(yàn)證:能否正常插入數(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);
}
/**
* 驗(yàn)證:線程池子線程中能否正常插入數(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);
}
/**
* 驗(yàn)證:正常插入數(shù)據(jù),拋出異常后,注解事務(wù)是否回滾
*/
@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();
}
}
/**
* 驗(yàn)證:正常插入數(shù)據(jù),拋出異常后,手動事務(wù)是否回滾
*/
@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() + "事務(wù)提交");
} catch (Exception e) {
e.printStackTrace();
transactionManager.rollback(transaction);
System.out.println("---------------------" + Thread.currentThread().getName() + "事務(wù)回滾");
}
}
/**
* 驗(yàn)證:主線程事務(wù),是否能影響到子線程事務(wù)
*/
@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 + "事務(wù)回滾");
} else {
System.out.println("---------------------id:" + id + "事務(wù)提交");
}
// 主線程事務(wù)未結(jié)束
// 實(shí)際主線程事務(wù)回滾了,但子線程事務(wù)未回滾
}
}
}
/**
* 驗(yàn)證:主線程事務(wù),未能影響到子線程事務(wù),是因?yàn)樽泳€程的事務(wù)傳播行為影響
*/
@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 + "事務(wù)回滾");
} else {
System.out.println("---------------------id:" + id + "事務(wù)提交");
}
// 主線程事務(wù)未結(jié)束
// 實(shí)際主線程事務(wù)回滾了,但子線程事務(wù)未回滾
}
}
}
/**
* 驗(yàn)證:主線程事務(wù),未能影響到子線程事務(wù)
* 主線程手動控制事務(wù),與注解自動控制事務(wù),結(jié)果是否依然是,主線程事務(wù)不能影響到子線程事務(wù)
*/
@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() + "事務(wù)提交");
} catch (Exception e) {
e.printStackTrace();
transactionManager.rollback(transaction);
System.out.println("---------------------" + Thread.currentThread().getName() + "事務(wù)回滾");
}
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 + "事務(wù)回滾");
} else {
System.out.println("---------------------id:" + id + "事務(wù)提交");
}
// 主線程事務(wù)已結(jié)束
// 主線程事務(wù)回滾了,但子線程事務(wù)未回滾
}
}
/**
* 驗(yàn)證:主線程事務(wù),未能影響到子線程事務(wù)
* 主線程手動控制事務(wù),子線程也手動控制事務(wù),結(jié)果是否依然是,主線程事務(wù)不能影響到子線程事務(wù)
*/
@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() + "事務(wù)提交");
} catch (Exception e) {
e.printStackTrace();
transactionManager.rollback(transaction);
System.out.println("---------------------" + Thread.currentThread().getName() + "事務(wù)回滾");
}
}
});
}
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() + "事務(wù)提交");
} catch (Exception e) {
e.printStackTrace();
transactionManager.rollback(transaction);
System.out.println("---------------------" + Thread.currentThread().getName() + "事務(wù)回滾");
}
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 + "事務(wù)回滾");
} else {
System.out.println("---------------------id:" + id + "事務(wù)提交");
}
// 主線程事務(wù)已結(jié)束
// 主線程事務(wù)回滾了,但子線程事務(wù)未回滾
}
}
/**
* 驗(yàn)證結(jié)果:主線程事務(wù)不能影響到子線程事務(wù)
* <p>
* 主線程,子線程控制各自事務(wù),等待一起提交
*/
@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í)行失敗,等待事務(wù)回滾");
flag.set(false);
} finally {
System.out.println("---------------" + Thread.currentThread().getName() + "--等待");
cdl.countDown();
cdl.await();
if (flag.get()) {
System.out.println("---------------" + Thread.currentThread().getName() + "--提交事務(wù)");
transactionManager.commit(transaction);
} else {
System.out.println("---------------" + Thread.currentThread().getName() + "--回滾事務(wù)");
transactionManager.rollback(transaction);
}
System.out.println("---------------" + Thread.currentThread().getName() + "--End");
}
}
});
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("---------------" + Thread.currentThread().getName() + "--執(zhí)行失敗,等待事務(wù)回滾");
flag.set(false);
} finally {
System.out.println("---------------" + Thread.currentThread().getName() + "--等待");
cdl.await();
if (flag.get()) {
System.out.println("---------------" + Thread.currentThread().getName() + "--提交事務(wù)");
transactionManager.commit(transaction);
} else {
System.out.println("---------------" + Thread.currentThread().getName() + "--回滾事務(wù)");
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 + "事務(wù)回滾");
} else {
System.out.println("---------------------id:" + id + "事務(wù)提交");
}
// 主線程事務(wù)已結(jié)束
// 主線程事務(wù)回滾了,子線程事務(wù)也回滾
}
}
/**
* 驗(yàn)證結(jié)果:主線程事務(wù)不能影響到子線程事務(wù)
* <p>
* 主線程,子線程控制各自事務(wù),等待一起提交
* 驗(yàn)證,主線程異常,子線程未異常,事務(wù)都回滾了
*/
@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());
// 最后一個提交的任務(wù),拋出異常;注釋掉會全部完成,否則全部回滾
// 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í)行失敗,等待事務(wù)回滾");
flag.set(false);
} finally {
System.out.println("---------------" + Thread.currentThread().getName() + "--等待");
cdl.countDown();
cdl.await();
if (flag.get()) {
System.out.println("---------------" + Thread.currentThread().getName() + "--提交事務(wù)");
transactionManager.commit(transaction);
} else {
System.out.println("---------------" + Thread.currentThread().getName() + "--回滾事務(wù)");
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í)行失敗,等待事務(wù)回滾");
flag.set(false);
} finally {
System.out.println("---------------" + Thread.currentThread().getName() + "--等待");
cdl.await();
if (flag.get()) {
System.out.println("---------------" + Thread.currentThread().getName() + "--提交事務(wù)");
transactionManager.commit(transaction);
} else {
System.out.println("---------------" + Thread.currentThread().getName() + "--回滾事務(wù)");
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 + "事務(wù)回滾");
} else {
System.out.println("---------------------id:" + id + "事務(wù)提交");
}
// 主線程事務(wù)已結(jié)束
// 主線程事務(wù)回滾了,子線程事務(wù)也回滾
}
}
/**
* 驗(yàn)證結(jié)果:主線程事務(wù)不能影響到子線程事務(wù)
* <p>
* 主線程,子線程控制各自事務(wù),等待一起提交
* 驗(yàn)證,主線程未異常,子線程異常,事務(wù)都回滾了
*/
@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());
// 最后一個提交的任務(wù),拋出異常;注釋掉會全部完成,否則全部回滾
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í)行失敗,等待事務(wù)回滾");
flag.set(false);
} finally {
System.out.println("---------------" + Thread.currentThread().getName() + "--等待");
cdl.countDown();
cdl.await();
if (flag.get()) {
System.out.println("---------------" + Thread.currentThread().getName() + "--提交事務(wù)");
transactionManager.commit(transaction);
} else {
System.out.println("---------------" + Thread.currentThread().getName() + "--回滾事務(wù)");
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í)行失敗,等待事務(wù)回滾");
flag.set(false);
} finally {
System.out.println("---------------" + Thread.currentThread().getName() + "--等待");
cdl.await();
if (flag.get()) {
System.out.println("---------------" + Thread.currentThread().getName() + "--提交事務(wù)");
transactionManager.commit(transaction);
} else {
System.out.println("---------------" + Thread.currentThread().getName() + "--回滾事務(wù)");
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 + "事務(wù)回滾");
} else {
System.out.println("---------------------id:" + id + "事務(wù)提交");
}
// 主線程事務(wù)已結(jié)束
// 主線程事務(wù)回滾了,子線程事務(wù)也回滾
}
}
}到此這篇關(guān)于Spring 多線程事務(wù)控制的實(shí)踐的文章就介紹到這了,更多相關(guān)Spring 多線程事務(wù)控制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
idea創(chuàng)建包含多個springboot module的maven project的方法
這篇文章主要介紹了idea創(chuàng)建包含多個springboot module的maven project的方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09
將Java程序的輸出結(jié)果寫入文件方法實(shí)例
Java使用正則表達(dá)式截取重復(fù)出現(xiàn)的XML字符串功能示例
Java中HashMap和Hashtable的區(qū)別小結(jié)
解決lambda表達(dá)式內(nèi)出現(xiàn)異常無法throw拋出的問題
java使用google身份驗(yàn)證器實(shí)現(xiàn)動態(tài)口令驗(yàn)證的示例
java實(shí)現(xiàn)微信掃碼登錄第三方網(wǎng)站功能(原理和代碼)

