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

业务:

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

需求:

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

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

设计:

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

猜你喜欢

转载自www.cnblogs.com/zhutouying/p/11672327.html