SpringBoot多线程——排队叫号实验模拟(二)
SpringBoot多线程——排队叫号模拟实验(二)
1. 前言
本文是前面一篇文章的续集。与之前的思路略有出入。
先来做个回顾,体检中心需要模拟客户多次排队叫号的流程,现在提出如下图所示的解决方案。Thread A是异步单线程,主要负责从客户信息List中取出一个人来,按一定间隔时间放入缓冲池里面;Thread B是异步单线程,主要负责从缓冲池中提取优先级最大的客户,通过计算判断出一个局部最优解的科室,将客户分配至该科室的队列等候体检(也就是分配到redis队列中);ThreadC是异步多线程,但我希望每个线程只做自己的事情,即每个线程模拟一个科室,按传入的参数从代表自己的科室等待队列里面捞人,然后睡眠一段时间作为模拟检查时间,最后再次进行计算,将客户分配到计算结果科室的队列。直到最后所有客户全部完成体检为止。
2. Thread C的逻辑代码
/**
* 通过此方法开启多个线程模拟科室叫号完成,并再次自动分配
* @param roomCode
* @param stayTime
*/
@Async("RoomTaskExecutor")
@Override
public void checkRoomScheduleTemplate(String roomCode, long stayTime) {
log.info("启动时间: {}", new Date());
QueryWrapper qw = new QueryWrapper();
qw.eq("room_code", roomCode);
RoomInfo currentRoom = roomInfoService.getOne(qw);
while (true) {
if (queueUtil.getQueueLength(roomCode) != 0L) {
// 有人就取,没人就一直监听
// 取出队首
PatientVo vo = queueUtil.headLeaveQueue(roomCode);
// 更新叫号时间
checkRecordDBUpdateCallingTime(new Date(), vo.getPatientId(), currentRoom.getRoomId());
// 存入检查科室
roomUtil.set2Room(roomCode, vo);
// 开始检查逗留
try {
log.info("{}时刻, {}在{}开始检查", new Date(), vo.getPatientName(), currentRoom.getRoomName());
Thread.sleep(stayTime);
log.info("{}时刻,{}在{}完成检查.", new Date(), vo.getPatientName(), currentRoom.getRoomName());
} catch (InterruptedException e) {
e.printStackTrace();
}
// 完成逗留,开始完成体检业务
// 保存完成时间
checkRecordDBUpdateFinishedTime(new Date(), vo.getPatientId(), currentRoom.getRoomId());
// 计算下一科室
RoomInfo nextRoom = algorithmService.computeNextRoom(patientInfoService.getById(vo.getPatientId()));
if (nextRoom == null) {
String nextMsg = vo.getQueueNum() + vo.getPatientName() + " 下一科室: 采样室留样后前台交表";
// 写入redis
stringRedisTemplate.opsForHash().put("nextCheck", roomCode, nextMsg);
// 添加到完成表里面,并结束本轮循环
log.info("{}时刻, {}完成了体检!", new Date(), vo.getPatientName());
continue;
// TODO: 实验结束
}
// 前端下一检查消息创建
String nextMsg = vo.getQueueNum() + vo.getPatientName() + " 下一科室: " + nextRoom.getRoomName();
// 写入redis
stringRedisTemplate.opsForHash().put("nextCheck", roomCode, nextMsg);
queueUtil.addPatient2Queue(nextRoom.getRoomCode(), vo);
// 科室正在检查可以更换为空了
roomUtil.set2Room(roomCode, new PatientVo());
// 添加入队记录
CheckRecord record = new CheckRecord(null, vo.getPatientId(), nextRoom.getRoomTypeId(), nextRoom.getRoomId(), new Date(), null, null, Boolean.FALSE);
checkRecordDBSave(record);
}
}
}
3. 总结与反思
SpringBoot底层怎么做到的多线程还是没有探究清楚,目前就是瞎猫碰上个死老鼠一样试出来的效果,不保证后续稳定安全,而且while死循环一直没被打破是个问题,而且在多线程中使用Thread.sleep并不好,所以后面有新方案改进再拿出来批评修正。最后说说部分博客文章,线程池配置的注释写错了导致我以为有了新发现,真的应该多看一手材料的,但信息检索能力太差了,基础也太差了,暂时先把项目做完,然后逐步提升基础吧。