深入理解 Java 中 ScheduledThreadPoolExecutor 的使用

1 概述

本文将详细分析 ScheduledThreadPoolExecutor 的使用,具体内容如下

  1. ScheduledThreadPoolExecutor 的初始化
  2. scheduleWithFixedDelay 方法和 scheduleAtFixedRate 方法详解
  3. 使用场景分析

2 ScheduledThreadPoolExecutor 的初始化

首先看看 ScheduledThreadPoolExecutor 的类图

image

2.1 ScheduledThreadPoolExecutor 构造方法

  1. public ScheduledThreadPoolExecutor(int corePoolSize)
  2. public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory)
  3. public ScheduledThreadPoolExecutor(int corePoolSize, RejectedExecutionHandler handler)
  4. public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory,RejectedExecutionHandler handler)

2.2 ScheduledThreadPoolExecutor 实例化

  1. 直接 new 出 ScheduledThreadPoolExecutor 类的实例
1
ScheduledThreadPoolExecutor scheduledExecutorService = new ScheduledThreadPoolExecutor(1);
  1. 通过 Executors 类的 newScheduledThreadPool 静态方法。
1
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);

3 scheduleWithFixedDelay 固定延迟执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import org.apache.commons.lang3.RandomUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Component
public class ScheuledCom {

private static final Logger logger = LoggerFactory.getLogger(ScheuledCom.class);

private static ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1);

@PostConstruct
public void init() {
logger.info("ScheuledCom init");
scheduledExecutorService.scheduleWithFixedDelay(() -> {
try {
logger.info("ScheuledCom start task");
long taskConsume = RandomUtils.nextLong(1, 5);
TimeUnit.SECONDS.sleep(taskConsume);
logger.info("ScheuledCom end task, consume = {}", taskConsume);
} catch (Exception e) {
}

}, 3, 2, TimeUnit.SECONDS);
}
}
  • 输出如下
1
2
3
4
5
6
7
8
9
10
11
15:08:52.249 [main] INFO  c.c.payment.component.ScheuledCom - ScheuledCom init
15:08:55.299 [pool-1-thread-1] INFO c.c.payment.component.ScheuledCom - ScheuledCom start task
15:08:59.302 [pool-1-thread-1] INFO c.c.payment.component.ScheuledCom - ScheuledCom end task, consume = 4
15:09:01.306 [pool-1-thread-1] INFO c.c.payment.component.ScheuledCom - ScheuledCom start task
15:09:03.307 [pool-1-thread-1] INFO c.c.payment.component.ScheuledCom - ScheuledCom end task, consume = 2
15:09:05.308 [pool-1-thread-1] INFO c.c.payment.component.ScheuledCom - ScheuledCom start task
15:09:06.308 [pool-1-thread-1] INFO c.c.payment.component.ScheuledCom - ScheuledCom end task, consume = 1
15:09:08.310 [pool-1-thread-1] INFO c.c.payment.component.ScheuledCom - ScheuledCom start task
15:09:10.310 [pool-1-thread-1] INFO c.c.payment.component.ScheuledCom - ScheuledCom end task, consume = 2
15:09:12.311 [pool-1-thread-1] INFO c.c.payment.component.ScheuledCom - ScheuledCom start task
15:09:13.312 [pool-1-thread-1] INFO c.c.payment.component.ScheuledCom - ScheuledCom end task, consume = 1
  • scheduleWithFixedDelay 方法参数解释如下:
  1. Runnable:任务接口对象,这里就是打印一下
  2. initialDelay:表示首次延迟多长时间执行
  3. delay:每次任务执行完毕后延迟多长时间后再次执行
  4. TimeUnit:initialDelay 和 delay 的时间单位
  • 关于 delay 参数需要注意以下问题:
  1. delay 的设置跟任务执行时间没有关系,这个参数表示任意两个任务执行的间隔时间

4 scheduleAtFixedRate 固定频率执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import org.apache.commons.lang3.RandomUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Component
public class ScheuledCom2 {

private static final Logger logger = LoggerFactory.getLogger(ScheuledCom2.class);

private static ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1);

@PostConstruct
public void init() {
logger.info("ScheuledCom2 init");
scheduledExecutorService.scheduleAtFixedRate(() -> {
try {
logger.info("ScheuledCom2 start task");
long taskConsume = RandomUtils.nextLong(1, 5);
TimeUnit.SECONDS.sleep(taskConsume);
logger.info("ScheuledCom2 end task, consume = {}", taskConsume);
} catch (Exception e) {
}

}, 3, 3, TimeUnit.SECONDS);
}
}
  • 输出如下
1
2
3
4
5
6
7
8
9
10
11
15:14:31.143 [main] INFO  c.c.payment.component.ScheuledCom2 - ScheuledCom2 init
15:14:34.194 [pool-2-thread-1] INFO c.c.payment.component.ScheuledCom2 - ScheuledCom2 start task
15:14:35.197 [pool-2-thread-1] INFO c.c.payment.component.ScheuledCom2 - ScheuledCom2 end task, consume = 1
15:14:37.193 [pool-2-thread-1] INFO c.c.payment.component.ScheuledCom2 - ScheuledCom2 start task
15:14:39.194 [pool-2-thread-1] INFO c.c.payment.component.ScheuledCom2 - ScheuledCom2 end task, consume = 2
15:14:40.193 [pool-2-thread-1] INFO c.c.payment.component.ScheuledCom2 - ScheuledCom2 start task
15:14:41.194 [pool-2-thread-1] INFO c.c.payment.component.ScheuledCom2 - ScheuledCom2 end task, consume = 1
15:14:43.193 [pool-2-thread-1] INFO c.c.payment.component.ScheuledCom2 - ScheuledCom2 start task
15:14:44.193 [pool-2-thread-1] INFO c.c.payment.component.ScheuledCom2 - ScheuledCom2 end task, consume = 1
15:14:46.194 [pool-2-thread-1] INFO c.c.payment.component.ScheuledCom2 - ScheuledCom2 start task
15:14:48.194 [pool-2-thread-1] INFO c.c.payment.component.ScheuledCom2 - ScheuledCom2 end task, consume = 2
  • scheduleAtFixedRate 方法参数解释如下:
  1. Runnable:任务接口对象,这里就是打印一下
  2. initialDelay:表示首次延迟多长时间执行
  3. period:任务的执行频率
  4. TimeUnit:initialDelay 和 period 的时间单位
  • 关于 period 参数需要注意以下问题:
  1. 如果 Runnable 任务的处理时长 大于 period,那么当 Runnable 任务执行完毕后会立即重复执行
  2. 如果 Runnable 任务的处理时长 小于或者等于 period,那么两个 Runnable 任务执行的时间间隔等于 period

5 使用建议

  1. ScheduledThreadPoolExecutor 对象用于周期性的执行任务,因此任务数最好和 corePoolSize 一致。
Buy me a cup of coffee