<1>: スレッドプールを構築する
/**
* 线程池
* <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;
}
}
<2>: 非同期処理が必要なメソッドを分離する
@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);
}
}
}
<3>
この非同期メソッドを呼び出す際の注意点: 分離された非同期メソッドと呼び出し元を同じカテゴリに含めることはできません。同じクラスに置く必要がある場合は、最初にそのクラスのプロキシ クラスを取得し、呼び出し分類メソッドを取得するためのプロキシ クラス
//发送人员数量过大时,异步处理
//异步处理时候,执行步骤一,然后执行步骤三,直接返回给前端 结果1, (步骤二与步骤三异步执行,其中步骤二是多线程处理,结果返回给前端时,步骤二不一定执行完毕了)
String userRole = userInfoDto.getRoleCode();//步骤一
subscriptionServiceExecutor.insertMessage(childNode, group, messageId, custInfo, rommId, rommName, subscriptionMsg);//步骤三
String roleSelect = userInfoDto.getRoleSelect();//步骤三
return 1;
オフトピック:
1: Tomcat のような Web サーバーの場合、すべてのリクエスト。スレッドが作成されます。グローバル変数については、スレッド セーフティの問題を考慮する必要があります。2
番目: 非同期処理が必要なビジネスの場合、子スレッドと親スレッドについてもスレッド セーフティの問題を考慮する必要があります。(親スレッドのメンバー変数は、子スレッドに対するグローバル変数です)
3: 非同期コードの場合、1 つのリクエストが完了するまでに 1 分かかります。1 分以内に、マルチスレッド処理 (自己処理) を使用して複数のリクエストが再度行われます。構成されたスレッド プール)、現時点では、親スレッドの変数には、子スレッドのスレッド セーフティの問題があります。