栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

ScheduledThreadPoolExecutor-并发编程(Java)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

ScheduledThreadPoolExecutor-并发编程(Java)

1、Timer

在认为调度线程池功能加入之前,可以使用java.util.Timer来实现定时功能。

  • 优点:简单易用

  • 缺点:由于所以认为都由同一个线程调度,因此所有任务都是串行执行

    • 前一个认为的延迟或者异常将会影响到之后的任务
  • 延迟示例:

    import lombok.extern.slf4j.Slf4j;
    
    import java.util.Timer;
    import java.util.TimerTask;
    import java.util.concurrent.TimeUnit;
    
    @Slf4j(topic = "c.TestTimer01")
    public class TestTimer01 {
        public static void main(String[] args) {
            Timer timer = new Timer();
    
            TimerTask task1 = new TimerTask() {
                @Override
                public void run() {
                    log.debug("task 1...");
                    try {
                        TimeUnit.SECONDS.sleep(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
    
            TimerTask task2 = new TimerTask() {
                @Override
                public void run() {
                    log.debug("task 2...");
                }
            };
    
            timer.schedule(task1, 1000);
            timer.schedule(task2, 1000);
        }
    }
    // 输出
    2021-12-16 09:04:57.752 DEBUG [main] c.TestTimer01 - start...
    2021-12-16 09:04:58.769 DEBUG [Timer-0] c.TestTimer01 - task 1...
    2021-12-16 09:05:00.776 DEBUG [Timer-0] c.TestTimer01 - task 2...
    
    • 任务2并没有如预期的延迟1s后开始执行,而是等待任务1执行完毕在执行
  • 异常示例

    import lombok.extern.slf4j.Slf4j;
    
    import java.util.Timer;
    import java.util.TimerTask;
    import java.util.concurrent.TimeUnit;
    
    @Slf4j(topic = "c.TestTimer02")
    public class TestTimer02 {
        public static void main(String[] args) {
            Timer timer = new Timer();
    
            TimerTask task1 = new TimerTask() {
                @Override
                public void run() {
                    log.debug("task 1...");
                    int i = 1 / 0;
                }
            };
    
            TimerTask task2 = new TimerTask() {
                @Override
                public void run() {
                    log.debug("task 2...");
                }
            };
            log.debug("start...");
            timer.schedule(task1, 1000);
            timer.schedule(task2, 1000);
        }
    }
    
    // 输出
    2021-12-16 09:07:13.722 DEBUG [main] c.TestTimer02 - start...
    2021-12-16 09:07:14.727 DEBUG [Timer-0] c.TestTimer02 - task 1...
    Exception in thread "Timer-0" java.lang.ArithmeticException: / by zero
    
    • 任务1异常,程序中断,并没有执行任务2
2、ScheduledThreadPoolExecutor 2.1、延迟执行任务

那么我们用ScheduledThreadPoolExecutor改造上面的示例,看看是否解决之前的问题?

import lombok.extern.slf4j.Slf4j;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Slf4j(topic = "c.TestScheduledThreadPool01")
public class TestScheduledThreadPool01 {
    public static void main(String[] args) {
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
        log.debug("start...");
        pool.schedule(() -> {
            log.debug("task 1...");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug("task 1 continue...");
            int i = 1 / 0;
            log.debug("task 1 end...");
        }, 1, TimeUnit.SECONDS);

        pool.schedule(() -> {
            log.debug("task 2...");
        }, 1, TimeUnit.SECONDS);
    }
}
// 输出
2021-12-16 09:27:27.703 DEBUG [main] c.TestScheduledThreadPool01 - start...
2021-12-16 09:27:28.752 DEBUG [pool-2-thread-1] c.TestScheduledThreadPool01 - task 1...
2021-12-16 09:27:28.752 DEBUG [pool-2-thread-2] c.TestScheduledThreadPool01 - task 2...
2021-12-16 09:27:30.756 DEBUG [pool-2-thread-1] c.TestScheduledThreadPool01 - task 1 continue...

我们发现问题得到解决,注意:如果线程池线程数设置为1 那么任务换是会串行执行。

但是我们又发现,明明任务1会出现执行异常,我们没有处理异常,但是异常不见了,并且任务没有结束,那么如何处理线程池异常呢?

2.2、定时执行任务
  • scheduleAtFixedRate(任务, 初始延时, 执行周期, 时间单位):执行周期就是每隔多次时间执行任务

  • scheduleWithFixedDelay(任务, 初始延时, 任务间隔, 时间单位):任务间隔为上次执行任务到本次执行任务之间的时间差

  • 示例:

    import lombok.extern.slf4j.Slf4j;
    
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    @Slf4j(topic = "c.TestScheduledThreadPool02")
    public class TestScheduledThreadPool02 {
        public static void main(String[] args) {
            ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
            log.debug("start...");
            method1(pool);
    
        }
    
        public static void method1(ScheduledExecutorService pool) {
            pool.scheduleAtFixedRate(() -> {
                log.debug("task 1 running...");
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, 1, 1, TimeUnit.SECONDS);
        }
    
        public static void method2(ScheduledExecutorService pool) {
    
            pool.scheduleWithFixedDelay(() -> {
                log.debug("task 2 running...");
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, 1, 1, TimeUnit.SECONDS);
        }
    
    
    }
    
    // 方法1输出结果
    2021-12-16 09:46:05.269 DEBUG [main] c.TestScheduledThreadPool02 - start...
    2021-12-16 09:46:06.343 DEBUG [pool-2-thread-1] c.TestScheduledThreadPool02 - task 1 running...
    2021-12-16 09:46:08.348 DEBUG [pool-2-thread-1] c.TestScheduledThreadPool02 - task 1 running...
    2021-12-16 09:46:10.354 DEBUG [pool-2-thread-2] c.TestScheduledThreadPool02 - task 1 running...
    // 方法2输出结果
    2021-12-16 09:49:49.959 DEBUG [main] c.TestScheduledThreadPool02 - start...
    2021-12-16 09:49:51.015 DEBUG [pool-2-thread-1] c.TestScheduledThreadPool02 - task 2 running...
    2021-12-16 09:49:54.024 DEBUG [pool-2-thread-1] c.TestScheduledThreadPool02 - task 2 running...
    2021-12-16 09:49:57.033 DEBUG [pool-2-thread-2] c.TestScheduledThreadPool02 - task 2 running...
    
    • 可以发现方法1任务间隔为2s,方法2任务间隔为3s
3、线程池异常处理
  • 方式1:任务自身处理异常,用try…catch…处理异常

  • 方式2:如果自己不处理,可以借助Future对象处理

  • 示例:

    import lombok.extern.slf4j.Slf4j;
    
    import java.util.concurrent.*;
    
    @Slf4j(topic = "c.TestScheduledThreadPool01")
    public class TestScheduledThreadPool03 {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
            log.debug("start...");
            ScheduledFuture future = pool.schedule(() -> {
                log.debug("task 1...");
                int i = 1 / 0;
                return true;
            }, 1, TimeUnit.SECONDS);
            System.out.println(future.get());
        }
    }
    // 输出
    2021-12-16 09:56:21.398 DEBUG [pool-2-thread-1] c.TestScheduledThreadPool01 - task 1...
    Exception in thread "main" java.util.concurrent.ExecutionException: 
    
4、定时任务应用

需求:我们系统需要每周4 4:00:00点定时维护,那么该如何做呢?

分析:

  1. 我们使用scheduleAtFixedRate方法

  2. 可以确定是任务周期为1周,那么初始延迟和时间单位如何确定呢?

  3. 既然任务周期为1周,那么我们需要知道当前时间和周4的时间间隔,即初始延时

  4. 当前时间可能在本周的周4之前,也可能之后。

  5. 为方便时间计算,我们用LocalDateTime类,既然要计算时间,那么单位为毫秒

代码:

import lombok.extern.slf4j.Slf4j;

import java.time.DayOfWeek;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;


@Slf4j(topic = "c.RegularMaintenance")
public class RegularMaintenance {
    public static void main(String[] args) {
        // 1、获取当前时间
        LocalDateTime now = LocalDateTime.now();
        // 2、确定第一个周4的时间
        LocalDateTime time = now.withHour(4).withMinute(0).withSecond(0).withNano(0).with(DayOfWeek.THURSDAY);
        // 如果当前时间晚于本周4 4:00:00,那么计算得出的时间在延后1周即可
        if (now.compareTo(time) > 0) {
            time = time.plusWeeks(1);
        }
        // 3、执行定时任务
        long initialDelay = Duration.between(now, time).toMillis();
        long period = 7 * 24 * 3600 * 1000;
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
        pool.scheduleAtFixedRate(() -> {
            log.debug("定时维护任务开始执行...");
            log.debug("定时维护任务结束...");
        }, initialDelay, period, TimeUnit.MILLISECONDS);
    }
}

参考视频:https://www.bilibili.com/video/BV16J411h7Rd 224~229

源代码仓库地址:https://gitee.com/gaogzhen/concurrent

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/672846.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号