• 多线程环境下保证实现单线程的案例


    业务:

    数据库用户与微信公众号实际关注用户的数据核对.

    需求:

    前端点击按钮[更新用户关注状态],后端启动线程异步更新数据,并且可以查询线程执行结果.

    数据更新过程不允许重复执行数据更新.

    设计:

    1,前端设计

    (1)点击按钮,ajax post请求启动异步线程更新数据,并调用执行结果接口;

    (2)执行结果接口,每隔2秒查询执行结果,直到执行结束.

    结合bootstrap模态框设计页面:

    <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">&times;</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>
    View Code

    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

    需要判断线程是否存在,存在则不允许更新数据;不存在则新建线程进行数据更新.保证当前只能有一个线程处理数据更新.

    @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;
        }
    }
    View Code

    (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

    @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);
        }
    }
    View Code

    (4)设计单例类保存线程执行结果

    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;
        }
    }
    View Code
  • 相关阅读:
    Vmware安装CentOS6
    MySQL Explain详解
    数据库——Redis超详细总结
    读书——你的见识决定你的人生
    软件——Ubuntu16.04设置静态ip地址
    软件——Hexo-NexT配置个人博客
    Java——Java实现生产者消费者
    程序员的踩坑经验总结(四):死锁
    程序员的踩坑经验总结(三):内存泄露
    认识自己和世界
  • 原文地址:https://www.cnblogs.com/zhutouying/p/11672327.html
Copyright © 2020-2023  润新知