在多线程下,统一提交各个子线程的任务。
先上代码
package security.study.mp.servers.Imp;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import security.study.mp.dao.IdworkMapper;
import security.study.mp.entity.Idwork;
import security.study.mp.servers.IdworkService;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* author : LuoPan
* date : 2021/8/11
*/
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class IdworkServiceImpl extends ServiceImpl<IdworkMapper, Idwork> implements IdworkService {
/**
* 注入线程池 从线程池中获取线程
*/
@Autowired
private ThreadPoolTaskExecutor testThreadPoll;
/**
* 数据库事务管理
*/
@Autowired
private DataSourceTransactionManager transactionManager;
/**
* dao 层 操作数据
*/
@Autowired
@SuppressWarnings("ALL")
private IdworkMapper idworkMapper;
// 锁
private final Lock lock = new ReentrantLock();
// 构建数据用 忽略
private final Random random = new Random(10);
@Override
public void manyThreadInsert() {
// 相当于一个 flag
AtomicBoolean atomicBoolean = new AtomicBoolean(true);
// 计数线程数
AtomicInteger atomicInteger = new AtomicInteger(0);
// 构建一个 外层(主线程的)Condition 通过这个 Condition 进入等待和 唤醒
Condition condition = lock.newCondition();
// 存入 子线程 的 Condition 装入到集合
AtomicReferenceArray<Condition> list = new AtomicReferenceArray<>(16);
// 线程 数量,第一次弄太大,导致异常
int size = 5;
// for 循环构建线程
for (int i = 0; i < size; i++) {
// 构建 子 线程的 Condition
Condition condition1 = lock.newCondition();
int finalI = i;
// 存入到 AtomicReferenceArray
list.set(finalI, condition1);
// 调用线程池进行 任务
testThreadPoll.execute(() -> {
log.info("线程内的finalI:{}", finalI);
// 这个是我自己构建的实体类
Idwork idwork = new Idwork();
idwork.setClassNo(random.nextInt());
idwork.setGradeYear(random.nextInt());
idwork.setName(random.nextInt() + "student");
idwork.setSex((short) 0);
// 数据库的事务
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// 3.设置事务隔离级别,开启新事务
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
// 4.获得事务状态
TransactionStatus status = transactionManager.getTransaction(def);
lock.lock();
try {
// 插入数据,不提交
idworkMapper.insert(idwork);
// 先加 atomicInteger 然后获取其值
int current = atomicInteger.addAndGet(1);
log.info(String.valueOf(current));
// 判断 是否都进行到待 提交数据阶段
if (current == size) {
// 唤醒 主线程
condition.signal();
}
// 当前线程 等待
list.get(finalI).await();
// 等待结束后 判断是否统一提交数据
if (atomicBoolean.get()) {
// 统一提交
transactionManager.commit(status);
} else {
// 统一失败 回滚
transactionManager.rollback(status);
}
} catch (Exception e) {
// 出现异常的 情况 设置回滚
atomicBoolean.set(false);
// 首先当前线程回滚
transactionManager.rollback(status);
} finally {
// 当前线程 释放锁
log.info("子线程完成!");
lock.unlock();
}
});
}
try {
lock.lock();
// 主 线程等待
condition.await();
// 被唤醒后 主 线程去 唤醒子线程 进行统一的 提交 或回滚
atomicBoolean.set(false);
for (int i = 0; i < size; i++) {
list.get(i).signal();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 当前线程释放锁
log.info("完成");
lock.unlock();
}
}
}
里面的 AtomicReferenceArray 为了应该可以换成 ArrayList ,没测试。
自己测试中出现的问题是。
1.开始时 线程数弄得高 ,结果中间获取sqlsession 连接出了问题,导致异常。线程开的不要太高。
2.理论上可能出现的情况 ,由于最后是for循环唤醒的,所以有可能导致一个唤醒,但是另一个没唤醒。
我的线程池
package security.study.config;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* author : LuoPan
* date : 2021/8/12
*/
@Component
@EnableAsync
public class ThreadPool {
private final static Integer CorePoolSize = 20;
private final static Integer MaxPoolSize = 40;
private final static Integer QueueCapacity = 99999;
private final static String ThreadNamePrefix = "pool";
@Bean(name = "testThreadPoll")
public ThreadPoolTaskExecutor creatExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(CorePoolSize);
executor.setMaxPoolSize(MaxPoolSize);
executor.setQueueCapacity(QueueCapacity);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
executor.setThreadNamePrefix(ThreadNamePrefix);
return executor;
}
}