定时任务循环处理(while和for等)、及分页等

文章讨论了使用while、for和dowhile等循环控制状态可变和不变数据的无限循环问题,强调了避免无限循环的方法,如检查页码和条数,以及提供了一种相对更优雅的代码实现方式。同时提到了无限循环对性能的影响和改进代码的挑战。
摘要由CSDN通过智能技术生成


饱经定时任务折磨。倒不是不会写定时任务,还是这个看似简单,实际有一些细节。

定时任务查出的条数可能不同,如何进行控制呢。

数据主要有几类:
纯查询数据
状态变化数据。
状态不变数据。

for循环

for循环是最基础的循环语句,每只程序猿对它都不陌生。

语法结构:

for(单次表达式;条件表达式;末尾循环体)
{
中间循环体;
}

最简单的for循环

for(;;){}
是的,你没看错,只有分号,表达式和循环体都是空的。这样也是正确的表达式,相当于无限循环。

表达式扩展(非常强大)

单次表达式扩展

单次表达式支持多条语句,,分隔即可。

for (int j =0,int k=5,int d=ddd();
	j<10 ;
	j++ ){
}

注:单词表达式定义变量,不可以引用外层变量,但是可以调用外层函数
实测应该也不能定义复杂类型。只能是int类型。

条件表达式扩展

条件表达式还可以加些自己的逻辑,而且不只一个条件,用逻辑符号连接即可
例如:循环中,页数>0 且 循环次数<1000(避免无限循环)。

for (int j =0; pages>0 && j<1000 ;j++ ){
}

注:条件表达式好像是定义不了多个变量,但是多个逻辑表达式可以共同作用,也是够用了。

末尾循环体扩展

和单次表达式相反,末尾循环体不支持变量定义,但是可以直接使用变量。
包括:单次表达式定义的变量,

纯查询数据

这样的比较好处理,最简单的处理方法,就是先查一次总分页信息。
然后按分页循环处理即可。代码:

// 先查询一次
PageHelper.startPage(request.getPageNum(), request.getPageSize());
List<User> Users = UserMapper.selectByExample(request);
PageInfo<User> pageInfo = new PageInfo<>(Users);

for (int i=0;i<pageInfo.getPages();i++) {
    int pageNum = pageInfo.getNavigatepageNums()[i];
    request.setPageNum(pageNum);
    List<User> Users = UserMapper.selectByExample(request);
}

这样功能没问题,就是看起来乱乱的,尤其是主逻辑代码比较多的时候。

纯查询数据优化后的代码

主要还是利用for循环的强大功能。代码:

User request = new User();
PageInfo<User> pageInfo = new PageInfo<>();
for (int i=0,pages=1;
        i<pages;
        i++,request.setPageNum(i+1),pages=pageInfo.getPages()) {

}

for循环之所以这么写,主要是为了易读。

状态可变数据(最优解)

每循环一次之后状态会变化,或者次数+1,或者其他标记值改变。

while进行控制

while (true){
    List<Entity> list= service.selectList();
    log.info("本次查出的数据:{}", JSONObject.toJSONString(list));
    if(list==null || list.size()==0){
        log.info("未查出数据结束任务");
        return ;
    }
    for(Entity item:list){
        // 业务逻辑
    }
}

这段代码通常来说没问题,但是也有注意点。
那就是循环的数据要保证状态变更。
即运行过一次后,一定要确保改变信息使下一次相同条件无法再查到。
否则就会出现无限循环的问题

例如,这次查到了,判断通过后,修改状态。但是因为判断条件始终不通过,状态一直没修改,那么这个while永远跑不完了,下次定时任务也永远无法执行。

另外,最好可以加上个计数器,记录循环的次数,可以做到心里有数。

状态不变数据

状态不进行任何改变,例如获取某个时间段的数据,进入到备份表。
因为没有状态改变,所以必须让每次的数据不同,否则就无限循环了。
可以pageNo的改变,超过pages就结束。 需要注意的是,一定要加分页信息。

注:状态不变的数据实际是有问题的,因为同条件可能会无限循环
解决方案:
1、for循环保证每次都自增。
2、可以引入redis避免重复操作。(特别是云端对次数有限制的数据)

while(true) 或者 while(flag) 或者 do while

其实都没太大区别。

while(true)写法

基本可以作为固定模板了。
这种写法,退出条件在代码中定义即可。
代码:

// 设置页码数
int pageNo = 0;
// 设置每页的数量
int pageSize = 100;
while(true){
    pageNo++;
    queryParam.setPageNo(pageNo);
    queryParam.setPageSize(pageSize);

    logger.info("循环次数:{}",pageNo);
    PageHelper.startPage(queryParam.getPageNo(), queryParam.getPageSize());
    List<User> queryResult = mapper.selectList(queryParam);
    PageInfo<User> pageInfo = new PageInfo<>(queryResult);
    logger.info("pageInfo简化版:{}",pageInfo.toStringSimple());
    int pages = pageInfo.getPages();
    //            100条 1页
    //            101条 2页
    if(pageNo>pages){ // 如果页码>总页数 结束循环
    break;
    }
    // 业务代码
}

pageInfo.toStringSimple()是为了打印出pageInfo除了list的信息,因为list信息比较多,不推荐打印,这里重写下PageInfo类即可。

do while写法

肯定也是可以的,pageNo和pages开始没有,给一个初始值,后面再赋值即可。

for用法

遇到个问题,在while中,setPage()无效,debug发现setPage是个空,这就导致永远都是操作第一页,肯定有问题啊,排查半天没查出来,不纠结了,改用for循环。

机制也比较简单,就是先查询总分页信息,然后再来个循环进行后续操作。
优点:机制容易掌握,for循环实际比while简单点,毕竟不用退出条件。
缺点:代码比较丑陋,毕竟先查一次只是为了分页。后续再循环,相当于写了两部分代码。
但是实测效果是可以的,代码丑就丑点吧。

后来又想了想,pages作为退出条件,可以赋一个值,然后在查询出来后,再赋实际值,就可以解决代码丑陋的问题了。
case:

public static void main(String[] args) {
    int pages=1;
    for (int j =0; j < pages; j++) {
        System.out.println("第"+j+"次查询");
        // pages重置 这里factPage就是业务查出的page
        Integer factPages=2;
        if(!ObjectUtils.isEmpty(factPages)&&factPages>1){
            pages=factPages;
        }
    }
}

总结

总的来说,就是一定要避免无限循环,可以通过页码比对,也可以通过条数比对。
如果条数不多,懒一点可以不用循环,只查一次,条数大些,例如10000条,基本也够用。

无限循环的危害

这样任务永远跑不玩,不但对性能有压力,后续的任务也不会再跑了,很坑。

以上方法虽然可以实现,但是总感觉不够优雅,有没有更优雅的代码呢?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值