并发编程之定时任务&定时线程池

定时线程池类的类结构图

在这里插入图片描述
用来处理定时任务或延时任务
加粗样式
它接收SchduledFutureTask类型的任务,是线程池调度任务的最小单位,有三种提交任务的方式:

  1. schedule
  2. scheduledAtFixedRate
  3. scheduledWithFixedDelay

延迟执行任务,或者先提交线程执行任务,得到返回值再执行下面的。异步阻塞,等上面的结果
在这里插入图片描述

scheduleAtFixedRate解析

看段代码

@Slf4j
public class ScheduThreadPool {
    static ScheduledThreadPoolExecutor scheduledThreadPoolExecutor=
                                                 new ScheduledThreadPoolExecutor(1);
    public static void main(String[] args) {
        scheduledThreadPoolExecutor.scheduleAtFixedRate(()->{
            log.info("send heart beat");
        },1000,2000, TimeUnit.MILLISECONDS);
    }
}

运行结果:
在这里插入图片描述
延迟1秒,每2秒执行一次。我们继续看

@Slf4j
public class ScheduThreadPool {
    static ScheduledThreadPoolExecutor scheduledThreadPoolExecutor=
    									new ScheduledThreadPoolExecutor(1);
    public static void main(String[] args) {
        scheduledThreadPoolExecutor.scheduleAtFixedRate(()->{
            log.info("send heart beat");
            Long startTime=System.currentTimeMillis(),nowtime=startTime;
            while ((nowtime-startTime)<5000){
                nowtime=System.currentTimeMillis();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.info("task over");
        },1000,2000, TimeUnit.MILLISECONDS);
    }
}

运行结果:
在这里插入图片描述
2秒的周期,执行任务的时间需要5秒。5秒执行完会立刻执行下一个任务。避免产生大量的堆积

scheduleWithFixedDelay

@Slf4j
public class ScheduThreadPool {
    static ScheduledThreadPoolExecutor scheduledThreadPoolExecutor=
    									new ScheduledThreadPoolExecutor(1);
    public static void main(String[] args) {
        scheduledThreadPoolExecutor.scheduleWithFixedDelay(()->{
            log.info("send heart beat");
            Long startTime=System.currentTimeMillis(),nowtime=startTime;
            while ((nowtime-startTime)<5000){
                nowtime=System.currentTimeMillis();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.info("task over");
        },1000,2000, TimeUnit.MILLISECONDS);
    }
}

运行结果:
在这里插入图片描述
任务5秒钟执行完,还需再等2秒再执行下一个任务。相当于7秒执行1次。

 如果出现异常
@Slf4j
public class ScheduThreadPool {
    static ScheduledThreadPoolExecutor scheduledThreadPoolExecutor
    					=new ScheduledThreadPoolExecutor(1);
    public static void main(String[] args) {
        scheduledThreadPoolExecutor.scheduleWithFixedDelay(()->{
            log.info("send heart beat");
            Long startTime=System.currentTimeMillis(),nowtime=startTime;
            while ((nowtime-startTime)<5000){
                nowtime=System.currentTimeMillis();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.info("task over");
            throw new RuntimeException("unexpect error stop");
        },1000,2000, TimeUnit.MILLISECONDS);
    }
}

在这里插入图片描述
则执行完一次就不会继续了。任务已经丢了,线程没有可执行的任务。

延迟类的任务一定要做异常的捕获。强行抛异常任务会丢失。

timer

要定期执行任务jdk里有没有工具类,有timer类,但是不推荐用,举个例子

  Timer time=new Timer();
  time.scheduleAtFixedRate(new TimerTask(){
      @Override
      public void run() {
         log.info("heart start");
          throw new RuntimeException("unexpect error stop");
      }
  },1000,2000);
  try {
      Thread.sleep(2000);
  } catch (InterruptedException e) {
      e.printStackTrace();
  }
  time.scheduleAtFixedRate(new TimerTask(){
      @Override
      public void run() {
          log.info("heart start");
          throw new RuntimeException("unexpect error stop");
      }
  },1000,2000);

在这里插入图片描述
Timer是单线程任务。

private final TimerThread thread = new TimerThread(queue);

在这里插入图片描述

线程都没了自然不会执行第2个timer任务。很容易导致程序挂掉,这也是阿里规范里明确要求不能用的。
对比一下线程池模式

   scheduledThreadPoolExecutor.scheduleWithFixedDelay(()->{
       log.info("send heart beat");
       Long startTime=System.currentTimeMillis(),nowtime=startTime;
       while ((nowtime-startTime)<5000){
           nowtime=System.currentTimeMillis();
           try {
               Thread.sleep(100);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
       log.info("task over");
       throw new RuntimeException("unexpect error stop");
   },1000,2000, TimeUnit.MILLISECONDS);

   scheduledThreadPoolExecutor.scheduleWithFixedDelay(()->{
       log.info("send heart beat");
       Long startTime=System.currentTimeMillis(),nowtime=startTime;
       while ((nowtime-startTime)<5000){
           nowtime=System.currentTimeMillis();
           try {
               Thread.sleep(100);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
       log.info("task over");
   },1000,2000, TimeUnit.MILLISECONDS);
运行结果

在这里插入图片描述
可以看到第2个线程的定时不会受到影响。

使用场景

分布式锁-redis实现

先解释下setnx

只在键 key 不存在的情况下, 将键 key 的值设置为 value

若键 key 已经存在, 则 SETNX 命令不做任何动作。

SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。

返回值
命令在设置成功时返回 1 , 设置失败时返回 0 。
代码示例

redis> EXISTS job                # job 不存在
(integer) 0

redis> SETNX job "programmer"    # job 设置成功
(integer) 1

redis> SETNX job "code-farmer"   # 尝试覆盖 job ,失败
(integer) 0

redis> GET job                   # 没有被覆盖
"programmer"

回归正题
redis做分布式锁性能更好

if(setnx("",锁的过期时间){ //拿到分布式锁
  //执行逻辑,查db,进行排序等等
}else{
  park,sleep
}

会出问题这段代码,如果app拿到锁挂了,我们一般设置过期时间。假设锁的过期时间是5秒钟,如果在5秒钟的时间内没执行完,锁又失效了,其他app可以拿到锁。换句话说锁的过期时间是不确定的。

可以采用定时线程池,假设5秒钟过期。每隔2秒钟去定时续约(续命)一次。看一下redis锁在不在,如果还在则延迟,直到逻辑执行完释放。因为不确定业务逻辑执行多少时间。
在这里插入图片描述

注册中心Eureka

微服务服务与注册发现中心Eureka

假设电商项目,存在订单服务,库存服务,消息服务,产品管理服务。通常下订单需要调好几个服务。
看下图
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值