<一>:构建线程池
/**
* 线程池
* <p>
* (1)判断核心线程数是否已满,核心线程数大小和corePoolSize参数有关,未满则创建线程执行任务
* (2)若核心线程池已满,判断队列是否满,队列是否满和workQueue参数有关,若未满则加入队列中
* (3)若队列已满,判断线程池是否已满,线程池是否已满和maximumPoolSize参数有关,若未满创建线程执行任务
* (4)若线程池已满,则采用拒绝策略处理无法执执行的任务,拒绝策略和handler参数有关
*
*/
@Configuration
@EnableAsync
public class AsyncExecutorConfig {
@Bean(name = "commonAsyncPool")
// 优先考虑,优先考虑被注解的对象注入
public Executor commonAsyncPool() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 线程池创建时候初始化的线程数
executor.setCorePoolSize(30);
// 线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
executor.setMaxPoolSize(150);
// 用来缓冲执行任务的队列
executor.setQueueCapacity(50);
// 允许线程的空闲时间60秒:当超过了核心线程之外的线程,在空闲时间到达之后会被销毁
executor.setKeepAliveSeconds(60);
// 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
executor.setThreadNamePrefix("serverCommonAsyncPool-");
// 线程池对拒绝任务的处理策略:这里采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,
// 该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
<二>:分离出来需要异步处理的方法
@Slf4j
@Service
@EnableAsync
public class SubscriptionServiceExecutor {
@Autowired
SwsJcCustTmRelativeMapper swsJcCustTmRelativeMapper;
@Async("commonAsyncPool")
public void insertMessage(List<MessageChildDto> childNode, MessageGroupDto group, Integer messageId, UserInfoDto custInfo, Integer rommId,
String rommName, SwsJcSubscriptionMsg subscriptionMsg) {
Map keyMap = new HashMap();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
for (MessageChildDto child : childNode) {
try {
if (!keyMap.containsKey(child.getChildId())) {
keyMap.put(child.getChildId(), child.getName());
SwsJcSubscriptionReceive subscriptionReceive = new SwsJcSubscriptionReceive();
subscriptionReceive.setReceiveGroup(group.getName());
subscriptionReceive.setMessageId(messageId);
if (custInfo.getSourceWay() == null || (!"Android".equals(custInfo.getSourceWay()) && !"iOS".equals(custInfo.getSourceWay()))) {
subscriptionReceive.setReceiveUser(child.getChildId());
} else {
subscriptionReceive.setReceiveUser(child.getKey());
}
subscriptionReceive.setType(0);
subscriptionReceive.setDelStatus(1);//未删除
subscriptionReceive.setReceiveUserName(child.getName());
subscriptionReceive.setAliasId(child.getKey());
AppRoleEnum appRoleEnum = AppRoleEnum.getEnumByType(group.getName());
if (appRoleEnum != null) {
RoleEnum roleEnum = RoleEnum.getRoleEnumByType(appRoleEnum.getApp());
if (roleEnum != null) {
subscriptionReceive.setReceiveRole(roleEnum.getTypeName());
}
}
if (rommId != 0) {
subscriptionReceive.setRoomId(rommId);
}
subscriptionReceive.setRoomName(rommName);
swsJcSubscriptionReceiveMapper.insertSelective(subscriptionReceive);
if (custInfo.getSourceWay() == null || (!"Android".equals(custInfo.getSourceWay()) && !"iOS".equals(custInfo.getSourceWay()))) {
//发送消息
SwsYwSubscriptionInfo subscribeInfo = new SwsYwSubscriptionInfo();
subscribeInfo.setTmplId("KfgRqIRTrZeS5D4ABm8_68Y4bUrJsXVMsZdbnm04-3s");
subscribeInfo.setOpenId(subscriptionReceive.getReceiveUser());
SwsYwSubscriptionInfo scriptionInfo = swsYwSubscriptionInfoMapper.selectByPrimaryKey(subscribeInfo);
if (scriptionInfo != null) {
try {
Map<String, String> data = new HashMap<>();
data.put("time3", sf.format(subscriptionMsg.getSendDate()));
data.put("thing8", subscriptionMsg.getTheme());
data.put("name9", subscriptionMsg.getSendUserName());
Map res_map = sendSubscribeMessage(subscriptionReceive.getReceiveUser(), subscribeInfo.getTmplId(), "pages/messageAll/index", data);
if (!"ok".equals(res_map.get("errmsg"))) {
}
} catch (Exception e) {
}
swsYwSubscriptionInfoMapper.deleteByPrimaryKey(subscribeInfo);
}
}
}
} catch (Exception e) {
log.error("消息发送失败:{}" + child.toString() + "/" + messageId);
}
}
}
<三>调用该异步方法
注意:分离出来的异步方法与调用者不要在同一个类种,如果需要放入同一个类中,需要先获取该类的代理类,通过代理类取调用分类出来的方法
//发送人员数量过大时,异步处理
//异步处理时候,执行步骤一,然后执行步骤三,直接返回给前端 结果1, (步骤二与步骤三异步执行,其中步骤二是多线程处理,结果返回给前端时,步骤二不一定执行完毕了)
String userRole = userInfoDto.getRoleCode();//步骤一
subscriptionServiceExecutor.insertMessage(childNode, group, messageId, custInfo, rommId, rommName, subscriptionMsg);//步骤三
String roleSelect = userInfoDto.getRoleSelect();//步骤三
return 1;
题外:
一:对于tomcat这种web服务器而言,每一次请求。都会创建一条线程,对于全局变量,需要考虑线程安全问题,
二:对于需要异步处理的业务,子线程与父线程也需要考虑线程安全问题。(父线程的成员变量相对于子线程就是全局变量)
三:对于异步的代码,单次请求需要1分钟完成,在1分钟之内,再次请求多次,采用的是多线程处理(自行配置线程池),此时父线程的变量,对于子线程而言,存在线程安全问题。