多线程环境下保证实现单线程的案例
业务:
数据库用户与微信公众号实际关注用户的数据核对.
需求:
前端点击按钮[更新用户关注状态],后端启动线程异步更新数据,并且可以查询线程执行结果.
数据更新过程不允许重复执行数据更新.
设计:
1,前端设计
(1)点击按钮,ajax post请求启动异步线程更新数据,并调用执行结果接口;
(2)执行结果接口,每隔2秒查询执行结果,直到执行结束.
结合bootstrap模态框设计页面:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<input type="button" value="更新用户关注状态" id="updateSubscribe"/> <!-- 模态框(Modal) --> <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> <h4><span class="label bg-danger">更新微信用户关注状态</span></h4> </div> <div class="modal-body"> <section class="panel text-center"> <a class="btn btn-circle btn-facebook btn-large"><i class="icon-facebook"></i></a> <div class="h4">正在核对微信用户关注公众号状态...请稍等!关闭窗口不影响业务.</div> <div class="line m-l m-r"></div> <h4 class="text-info"><strong id="msg">正在刷新...</strong></h4> <small>* 蓝色文字代表执行结果</small> </section> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button> </div> </div><!-- /.modal-content --> </div><!-- /.modal --> </div>
JS实现如下:
$("#updateSubscribe").click(function(){ $('#myModal').modal('show'); $.ajax({ type: "POST", cache: false, url: "/updateSubscribe", dataType: 'json', success: function (data) { if (data.errcode!='0') { $("#msg").html(data['errmsg']); } updateResult(); } }); }); var updateResult = function(){ $.ajax({ type: "POST", cache: false, url: "/updateSubscribeResult", dataType: 'json', success: function (data) { if(data.success){ $("#msg").html(data.message); }else{ setTimeout(function () { updateResult(); }, 2000); } } }); }
2,后端设计
使用springboot搭建框架
(1)Controller
需要判断线程是否存在,存在则不允许更新数据;不存在则新建线程进行数据更新.保证当前只能有一个线程处理数据更新.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@Controller public class UpdateSubscribeController { @Autowired private UpdateSubscribeService updateSubscribeService; @PostMapping(value = "/updateSubscribe") @ResponseBody public ResultBean get(){ ResultBean resultBean = new ResultBean(); Thread thread = getThreadByName("UpdateSubscribe"); if(null == thread){ UpdateSubscribeThread updateSubscribeThread = new UpdateSubscribeThread(updateSubscribeService); updateSubscribeThread.setName("UpdateSubscribe"); updateSubscribeThread.start(); }else { resultBean.setError(ErrorCode.SYSTEM_ERROR); resultBean.setErrmsg("上一次更新尚未结束,请勿重复更新."); } return resultBean; } @PostMapping(value = "/updateSubscribeResult") @ResponseBody public JsonResult updateSubscribeResult(){ JsonResult result = new JsonResult(); result.setSuccess(false); Thread thread = getThreadByName("UpdateSubscribe"); if(null == thread){ // 更新完成 result.setSuccess(true); try { result.setMessage(UpdateSubscribeResultSingleton.getInstance().getErrorMsg()); }catch (Exception e){ e.printStackTrace(); } } return result; // 还没有执行完毕 } /** * 通过线程名字得到线程对象 * @param threadName * @return */ public static Thread getThreadByName(String threadName) { for (Thread t : Thread.getAllStackTraces().keySet()) { if (t.getName().equals(threadName)) { return t; } } return null; } }
(2)线程类UpdateSubscribeThread
public class UpdateSubscribeThread extends Thread{ private UpdateSubscribeService updateSubscribeService; public UpdateSubscribeThread(UpdateSubscribeService updateSubscribeService) { this.updateSubscribeService = updateSubscribeService; } @Override public void run(){ updateSubscribeService.checkSubscribe(); } }
(3)业务实现类UpdateSubscribeService
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@Service public class UpdateSubscribeService { private static final Logger log = LoggerFactory.getLogger(UpdateSubscribeService.class); @Autowired private WxMpService wxMpService; @Autowired private UserDao userDao; @Autowired private WxUserDao wxUserDao; @Autowired private EntityManagerFactory emf; public void checkSubscribe(){ log.info("start :: UpdateSubscribeService ==> checkSubscribe method: 执行异步任务 {}",Thread.currentThread().getName()); UpdateSubscribeResultSingleton resultSingleton = UpdateSubscribeResultSingleton.getInstance(); resultSingleton.setSuccess(null); try { // Thread.currentThread().sleep(7000); wxUserDao.deleteAll(); WxMpUserList wxMpUserList = wxMpService.userList(null); Criteria<User> userCriteria = new Criteria<>() ; userCriteria.add(Restrictions.eq("subscribe" ,true)) ; Long userCount = userDao.count(userCriteria); if(wxMpUserList.getTotal() > userCount ){ int check = 0; while(null != wxMpUserList){ check += wxMpUserList.getCount(); wxMpUserList = getNextWxMpUserList(wxMpUserList, check); } EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); Query query = em.createNativeQuery("UPDATE user SET subscribe=1 WHERE weixin IN(SELECT id FROM wx_user)"); int num = query.executeUpdate(); log.info("更新{}个用户状态为已关注",num); query = em.createNativeQuery("UPDATE user SET subscribe=0 WHERE weixin not IN(SELECT id FROM wx_user)"); int result2 = query.executeUpdate(); log.info("更新{}个用户状态为未关注",result2); em.getTransaction().commit(); query = em.createNativeQuery("SELECT wu.* FROM wx_user wu left join user u ON wu.id=u.weixin WHERE u.weixin IS NULL",WxUser.class); List<WxUser> list = query.getResultList(); Date nowTime = new Date(); for(WxUser wxUser : list){ String openId = wxUser.getId(); WxMpUser wxMpUser = wxMpService.userInfo(openId,"zh_CN"); log.info("手动补充关注用户::{} -> {}",openId,wxMpUser.getNickname()); User user = new User(); user.setWeixin(openId); user.setName(wxMpUser.getNickname()); user.setUnionId(wxMpUser.getUnionId()); user.setUrl(wxMpUser.getHeadImgUrl()); user.setCreateAt(nowTime); user.setLastLogin(nowTime); user.setSubscribe(true); userDao.saveAndFlush(user); } em.close(); } log.info("end :: UpdateSubscribeService ==> checkSubscribe method: 执行异步任务"); resultSingleton.setSuccess(true); resultSingleton.setErrorMsg("更新成功"); resultSingleton.setLastUpdateTime(new Date()); } catch (Exception e) { log.info("UpdateSubscribeService ==> checkSubscribe method: 执行异步任务失败,原因::{}",e.toString()); e.printStackTrace(); resultSingleton.setSuccess(false); resultSingleton.setErrorMsg("更新失败,原因::"+e.toString()); resultSingleton.setLastUpdateTime(new Date()); } } private WxMpUserList getNextWxMpUserList(WxMpUserList wxMpUserList , int check) throws WxErrorException { List<String> openIdList = wxMpUserList.getOpenIds(); List<WxUser> wxUserList = new ArrayList<>(); for(String openId : openIdList){ WxUser wxUser = new WxUser(); wxUser.setId(openId); wxUserList.add(wxUser); } wxUserDao.save(wxUserList); if(check == wxMpUserList.getTotal()){ return null; } return wxMpService.userList(null); } }
(4)设计单例类保存线程执行结果
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class UpdateSubscribeResultSingleton { private static class UpdateSubscribeResultHandler{ private static UpdateSubscribeResultSingleton intstance = new UpdateSubscribeResultSingleton(); } private UpdateSubscribeResultSingleton(){ } public static UpdateSubscribeResultSingleton getInstance(){ return UpdateSubscribeResultHandler.intstance; } private Boolean isSuccess; private String errorMsg ; private Date lastUpdateTime; public Boolean getSuccess() { return isSuccess; } public void setSuccess(Boolean success) { isSuccess = success; } public String getErrorMsg() { return errorMsg; } public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; } public Date getLastUpdateTime() { return lastUpdateTime; } public void setLastUpdateTime(Date lastUpdateTime) { this.lastUpdateTime = lastUpdateTime; } }