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并不好,所以后面有新方案改进再拿出来批评修正。最后说说部分博客文章,线程池配置的注释写错了导致我以为有了新发现,真的应该多看一手材料的,但信息检索能力太差了,基础也太差了,暂时先把项目做完,然后逐步提升基础吧。

参考文章

SpringBoot @Async 注解使用总结,配置系统线程池

posted @ 2022-03-02 17:03  cee_nil  阅读(386)  评论(1编辑  收藏  举报