Обучение Springboot — синхронизированные задачи и асинхронные вызовы

Spring Boot Java Spring

Статьи по Теме

SpringBoot изучает один

SpringBoot Learning 2: Работа с базой данных

SpringBoot изучает три: обработка исключений и ведение журнала

предисловие

Сегодня научимся реализовывать запланированную задачу и асинхронный вызов через SpringBoot.

задача на время

В Spring мы можем реализовать наши временные задачи с помощью двух аннотаций @EnableScheduling и @Scheduled Далее взгляните на эти две аннотации:

@EnableScheduling

После того, как аннотация @EnableScheduling отмечена в классе конфигурации, это означает, что Spring включил запланированные задачи, и методы, отмеченные аннотацией @Scheduled в контейнере Spring, будут обнаружены и выполнены; если требуется более детальное управление, настройте A класс может реализовать класс SchedulingConfigurer для этого следующим образом:

@Configuration
@EnableScheduling
public class MyScheduleTaskConfig implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
//        taskRegistrar.setCronTasks();
//        taskRegistrar.setFixedDelayTasks();
//        taskRegistrar.setTriggerTasksList();
    }

    @Bean(destroyMethod="shutdown")
    public Executor taskExecutor() {
         return Executors.newScheduledThreadPool(100);
     }
}

 @Scheduled

После того, как запланированная задача включена в Spring, вы можете использовать аннотацию @Scheduled, чтобы определить метод, который необходимо выполнять регулярно.Далее давайте взглянем на одно из его определений:

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {
    // cron 表达式
	String cron() default "";
    // 时区
	String zone() default "";
    // 时间间隔 单位毫秒
	long fixedDelay() default -1;
    // 时间间隔,单位毫秒,使用 String 表示
	String fixedDelayString() default "";
    // 时间间隔 单位毫秒
	long fixedRate() default -1;
    // 时间间隔,单位毫秒,使用 String 表示
	String fixedRateString() default "";
    // 初始化时间,即任务在多少秒后开始执行第一次
	long initialDelay() default -1;
    // 初始化时间,即任务在多少秒后开始执行第一次,使用 String 表示
	String initialDelayString() default "";

}


Далее давайте проверим три свойства cron, fixedDelay и fixedRate:

cron 

Атрибут cron, то есть использование выражений cron для управления запланированным выполнением задач, выглядит следующим образом:

/**
 * 定时任务
 */
@Component
public class ScheduledTask {

    private static final DateFormat dateFormat = new SimpleDateFormat("HH:m:ss");

    @Scheduled(cron = "0/5 * * * * ?")
    public void printTime3() throws InterruptedException {
        System.out.println("time: " + dateFormat.format(new Date()));
    }
}

Запускаем программу и видим вывод консоли, он будет печатать каждые 5 секунд:

time: 14:19:00
time: 14:19:05
time: 14:19:10
time: 14:19:15

Что делать, если задача была выполнена в течение 5 секунд?

    @Scheduled(cron = "0/5 * * * * ?")
    public void printTime3() throws InterruptedException {
        System.out.println("time: " + dateFormat.format(new Date()));
        Thread.sleep(2000);
    }

time: 14:20:50
time: 14:20:55
time: 14:21:00
time: 14:21:05

Из распечатанных результатов видно, что если задача была выполнена в течение интервала времени, следующая задача также будет ждать окончания интервала времени перед выполнением следующей задачи.

 

Что делать, если задача не была выполнена в течение 5 секунд?

    @Scheduled(cron = "0/5 * * * * ?")
    public void printTime3() throws InterruptedException {
        System.out.println("time: " + dateFormat.format(new Date()));
        Thread.sleep(8000);
    }

time: 14:28:25
time: 14:28:35
time: 14:28:45
time: 14:28:55

Как видно из распечатанных результатов, если задача не будет завершена в течение временного интервала, следующая задача будет выполнена в следующем цикле, потому что, когда Spring обрабатывает временную задачу с помощью выражений cron, она будет выполняться в задаче. В начале оценивается, может ли задача быть выполнена, если она может быть выполнена, если нет, то она не будет выполнять задачу и будет ждать следующего выполнения.

fixedDelay

Свойство fixedDelay представляет временной интервал от конца предыдущей задачи до начала следующей задачи.Если время выполнения предыдущей задачи истекло, следующая задача будет ждать завершения выполнения предыдущей задачи перед выполнением:

    @Scheduled(fixedDelay = 5000)
    public void printTime2() throws InterruptedException {
        System.out.println("time: " + dateFormat.format(new Date()));
    }

time: 14:38:29
time: 14:38:34
time: 14:38:39
time: 14:38:44
time: 14:38:49

Если задача выполняется в интервале:

    @Scheduled(fixedDelay = 5000)
    public void printTime2() throws InterruptedException {
        System.out.println("time: " + dateFormat.format(new Date()));
        Thread.sleep(2000);
    }

Печатать каждые 7 секунд

time: 14:40:24
time: 14:40:31
time: 14:40:38
time: 14:40:45
time: 14:40:52

Как видно из результатов печати, если задача была выполнена в пределах временного интервала, время ожидания следующей задачи будет равно времени выполнения задачи + временной интервал;

Если задание не выполнено в промежутке между:

    @Scheduled(fixedDelay = 5000)
    public void printTime2() throws InterruptedException {
        System.out.println("time: " + dateFormat.format(new Date()));
        Thread.sleep(8000);
    }

Печатать каждые 13 секунд

time: 14:44:20
time: 14:44:33
time: 14:44:46
time: 14:44:59
time: 14:45:12

Как видно из результатов печати, если задача не была выполнена в течение временного интервала, время ожидания следующей задачи будет равно времени выполнения задачи + временной интервал;

Как видно из приведенных выше двух примеров, fixedDelay в основном ориентируется на интервал времени между окончанием предыдущей задачи и началом следующей задачи, то есть время ожидания до выполнения следующей задачи = выполнение время предыдущей задачи + временной интервал

fixedRate

fixedRate представляет временной интервал между началом выполнения двух задач,

    @Scheduled(fixedRate = 5000)
    public void printTime() throws InterruptedException {
        System.out.println("time: " + dateFormat.format(new Date()));
    }

time: 14:53:38
time: 14:53:43
time: 14:53:48
time: 14:53:53
time: 14:53:58

Как и cron и fixedDelay выше, они выполняются каждые 5 секунд.

Если задача была выполнена в течение интервала времени, как показано ниже:

    @Scheduled(fixedRate = 5000)
    public void printTime() throws InterruptedException {
        System.out.println("time: " + dateFormat.format(new Date()));
        Thread.sleep(2000);
    }

time: 14:55:04
time: 14:55:09
time: 14:55:14
time: 14:55:19
time: 14:55:24

Видно, что это отличается от fixedDelay.Время ожидания следующей задачи fixedDelay равно времени выполнения предыдущей задачи + временной интервал, и здесь, как видно из распечатанных результатов, если предыдущая задача была завершена. После завершения выполнения следующая задача все еще будет ждать окончания временного интервала перед выполнением. Обратите внимание, что временной интервал, представленный fixedRate, представляет собой интервал времени между запуском двух задач.

Если задача не выполнена в течение временного интервала;

    @Scheduled(fixedRate = 5000)
    public void printTime() throws InterruptedException {
        System.out.println("time: " + dateFormat.format(new Date()));
        Thread.sleep(8000);
    }

time: 15:0:24
time: 15:0:32
time: 15:0:40
time: 15:0:48
time: 15:0:56

Как видно из распечатанных результатов, если задача не была выполнена в течение интервала времени, следующая задача будет ждать завершения выполнения предыдущей задачи, прежде чем начать выполнение.В это время интервал времени больше не 5 секунд, но 8 секунд.

В заключение

1. fixedRate подчеркивает, чтоВремя начала последней задачиприбыть время начала следующей задачиинтервал
2, фиксированная задержка регулируетсяВремя окончания последней задачиприбыть время начала следующей задачиинтервал
3.Выражение cron настраивает момент выполнения задачи.Определит можно ли выполнить задачу в начале задачи.Если можно то она будет выполнена.Если нельзя то выполнение будет пропущено.

Асинхронный вызов

В Spring Boot асинхронные вызовы реализуются с помощью двух аннотаций @EnableAsync и @Async, Далее взгляните на эти две аннотации:

@EnableAsync

@EnableAsync Эта аннотация в основном используется для включения асинхронных вызовов Spring, используемых в классах конфигурации,

@SpringBootApplication
@EnableAsync
public class MyspringbootApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyspringbootApplication.class, args);
    }
}

@Async

Когда Spring разрешает асинхронный вызов, вы можете использовать @Async для определения методов, которые должны выполняться асинхронно.

/**
 * 异步调用任务
 */
@Component
public class AsyncTask {

    private Random random = new Random();

    @Async
    public void task1() throws InterruptedException {
        System.out.println("任务一开始...");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        System.out.println("任务一耗时:" + (System.currentTimeMillis() - start));
        System.out.println("任务一完成...");
    }

    @Async
    public void task2() throws InterruptedException {
        System.out.println("任务二开始...");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        System.out.println("任务二耗时:" + (System.currentTimeMillis() - start));
        System.out.println("任务二完成...");
    }

    @Async
    public void task3() throws InterruptedException {
        System.out.println("任务三开始...");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        System.out.println("任务三耗时:" + (System.currentTimeMillis() - start));
        System.out.println("任务三完成...");
    }
}

тестовое задание:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = MyspringbootApplication.class)
public class MyTest {

    @Autowired
    private AsyncTask task;

    @Test
    public void test() throws InterruptedException {
        task.task1();
        task.task2();
        task.task3();
        Thread.sleep(60 * 1 * 1000);
    }
}

Миссия начинается...
Миссия третья начинается...
Миссия 2 начинается...
Задача 3 трудоемкая: 2018 г.
Миссия 3 завершена...
Миссия 1 раз: 4027
Как только миссия будет завершена...
Трудоемкая задача 2: 9364
2 миссия завершена...

 

Если вы не хотите использовать пул потоков по умолчанию, мы также можем настроить пул потоков, просто внедрив AsyncConfigurer:

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {

    @Bean(name = "taskExecutor")
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(7);
        executor.setMaxPoolSize(42);
        executor.setQueueCapacity(11);
        executor.setThreadNamePrefix("MyExecutor-");
        executor.initialize();
        // 关闭资源
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return null;
    }
}

Используйте собственный пул потоков:

    @Async("taskExecutor")
    public void task1() throws InterruptedException {
        System.out.println("任务一开始...");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        System.out.println("任务一耗时:" + (System.currentTimeMillis() - start));
        System.out.println("任务一完成...");
    }

 

Когда поток выполняется, может быть возвращаемое значение Будущее, мы можем вернуть AsyncResult:

    @Async("taskExecutor")
    public Future<String> test4(){
        System.out.println("执行任务,返回结果");
        return new AsyncResult<>("任务执行结果:5");
    }

тестовое задание:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = MyspringbootApplication.class)
public class MyTest {

    @Autowired
    private AsyncTask task;

    @Test
    public void test2() throws InterruptedException, ExecutionException, TimeoutException {
        Future<String> future = task.test4();
        System.out.println("任务是否完成:" + future.isDone());
        String result = future.get(5, TimeUnit.SECONDS);
        System.out.println(result);
        System.out.println("任务是否完成:" + future.isDone());
    }
}

результат:

Завершена ли задача: false
Выполнить задачу и вернуть результат
Результат выполнения задачи: 5
Завершена ли задача: true

Вышеупомянутое предназначено для выполнения синхронизированных задач и асинхронных вызовов через SpringBoot.