Java高并发编程中ScheduledExecutorService的使用及详细介绍-刘宇
作者:刘宇
CSDN博客地址:https://blog.csdn.net/liuyu973971883
有部分资料参考,如有侵权,请联系删除。如有不正确的地方,烦请指正,谢谢。
一、什么是ScheduledExecutorService?
ScheduledExecutorService是基于ExecutorService的功能实现的延迟和周期执行任务的功能。每个任务以及每个任务的每个周期都会提交到线程池中由线程去执行,所以任务在不同周期内执行它的线程可能是不同的。ScheduledExecutorService接口的默认实现类是ScheduledThreadPoolExecutor。在周期执行的任务中,如果任务执行时间大于周期时间,则会以任务时间优先,等任务执行完毕后才会进入下一次周期
二、ScheduledThreadPoolExecutor中的方法
因为ScheduledThreadPoolExecutor继承了ThreadPoolExecutor类,所以有很多方法都是来自ThreadPoolExecutor类的,这里就不做解释了,想了解的兄弟可以看我前面的博客:点击查看详细
1、构造方法
构造一个Schedule线程池,最大线程数为Integer的最大值,线程的空闲时间为0,队列采用的是DelayedWorkQueue
- corePoolSize:线程池核心线程数
- threadFactory:线程工厂
- handler:任务拒绝策略
ScheduledThreadPoolExecutor(int corePoolSize)
ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory)
ScheduledThreadPoolExecutor(int corePoolSize, RejectedExecutionHandler handler)
ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, RejectedExecutionHandler handler)
2、schedule方法
延时执行runnable或者callable任务。执行runnable任务时是没有结果返回的,那为什么还会返回ScheduledFuture,因为我们可以通过Future做一些取消任务等操作。
- delay:延时的时间
- unit:时间单位
ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
<V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)
3、scheduleAtFixedRate方法
固定周期性执行任务,当任务的执行时长大于周期,那么下一个周期任务将在上一个执行完毕之后马上执行。
- command:runnable任务
- initialDelay:任务首次执行前的延迟时间
- period:周期时间
- unit:时间单位
ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
4、scheduleWithFixedDelay方法
固定延时执行任务,也是周期性任务,和scheduleAtFixedRate不同的是:scheduleAtFixedRate当任务执行时间小于周期时间时,此时周期时间到了的时候会进入下一周期,如果任务执行时间大于周期时间时,任务结束后会立即进入下一周期;而scheduleWithFixedDelay是无论你任务时间是否超过,都将会在你任务执行完毕后延迟固定秒数,才会进入下一周期。
- command:runnable任务
- initialDelay:任务首次执行前的延迟时间
- delay:延时时间
- unit:时间单位
ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
5、setContinueExistingPeriodicTasksAfterShutdownPolicy方法
默认为false。在线程池执行shutdown方法后是否继续执行scheduleAtFixedRate方法和scheduleWithFixedDelay方法提交的任务
void setContinueExistingPeriodicTasksAfterShutdownPolicy(boolean value)
6、setExecuteExistingDelayedTasksAfterShutdownPolicy方法
默认为true,在线程池执行shutdown方法后,需要等待当前正在等待的任务的和正在运行的任务被执行完,然后进程被销毁。为false时,表示放弃等待的任务,正在运行的任务一旦完成,则进程被销毁。
void setExecuteExistingDelayedTasksAfterShutdownPolicy(boolean value)
三、练习
1、schedule练习
本次练习中出来schedule的练习外,还包含了如何取消任务。
package com.brycen.part3.threadpool;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ScheduledExecutorServiceExample {
public static void main(String[] args) {
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(2);
//2秒后执行runnable任务
scheduledThreadPoolExecutor.schedule(() -> {
System.out.println("This is runable1 task");
}, 2, TimeUnit.SECONDS);
//提交一个2秒后才执行的runnable任务
//既然runnable无法返回结果,为什么还要有Future呢,因为我们可以通过Future进行取消任务等操作
ScheduledFuture<?> runnableFuture = scheduledThreadPoolExecutor.schedule(() -> {
System.out.println("This is runable2 task");
}, 2, TimeUnit.SECONDS);
//取消任务
runnableFuture.cancel(true);
//休眠3秒,确保上面的任务都被执行完
mySleep(3);
System.out.println("========================");
}
private static void mySleep(int seconds){
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
This is runable1 task
========================
2、scheduleAtFixedRate练习1
周期性执行某个任务,执行到一定之间后取消任务
package com.test.part3.threadpool;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ScheduledExecutorServiceExample2 {
public static void main(String[] args) {
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(2);
//提交延迟1秒执行,周期为2秒的runnable任务,虽然runnable没有返回结果,但是可以通过runnable取消任务
ScheduledFuture<?> runnableFuture = scheduledThreadPoolExecutor.scheduleAtFixedRate(() -> {
System.out.println("This is runable task running "+Thread.currentThread().getName());
}, 1,2, TimeUnit.SECONDS);
//休眠8秒
mySleep(8);
//取消该循坏任务
runnableFuture.cancel(true);
}
private static void mySleep(int seconds){
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
- 可以看出每个周期执行的任务并不是同一个线程,周期时间到的时候只是将任务扔到线程池的任务队列中由空闲线程获取它的执行权。
This is runable task running pool-1-thread-1
This is runable task running pool-1-thread-1
This is runable task running pool-1-thread-1
This is runable task running pool-1-thread-2
3、scheduleAtFixedRate练习2
超时的周期性任务
package com.brycen.part3.threadpool;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
public class ScheduledExecutorServiceExample3 {
public static void main(String[] args) {
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(2);
AtomicLong atomicLong = new AtomicLong(0L);
//提交初始延迟1秒执行,固定周期为2秒的runnable任务
ScheduledFuture<?> runnableFuture = scheduledThreadPoolExecutor.scheduleAtFixedRate(() -> {
//记录当前时间
Long current = System.currentTimeMillis();
//判断是否为第一次运行
if (atomicLong.get()==0){
atomicLong.set(current);
System.out.printf("first running [%d]\n",atomicLong.get());
}else{
//记录与上次的间隔时间
System.out.printf("running time:[%d]\n",current-atomicLong.get());
}
//将当前时间保存
atomicLong.set(current);
//模拟超过固定周期时间
mySleep(5);
}, 1,2, TimeUnit.SECONDS);
}
private static void mySleep(int seconds){
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
- 可以看出,超出周期时间时,任务完成后立即就进入了下一周期
first running [1597659726690]
running time:[5042]
running time:[5001]
running time:[5000]
running time:[5001]
4、scheduleWithFixedDelay练习
package com.test.part3.threadpool;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
public class ScheduledExecutorServiceExample4 {
public static void main(String[] args) {
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(2);
AtomicLong atomicLong = new AtomicLong(0L);
//提交初始延迟1秒执行,延迟为2秒的runnable任务
ScheduledFuture<?> runnableFuture = scheduledThreadPoolExecutor.scheduleWithFixedDelay(() -> {
//记录当前时间
Long current = System.currentTimeMillis();
//判断是否为第一次运行
if (atomicLong.get()==0){
atomicLong.set(current);
System.out.printf("first running [%d]\n",atomicLong.get());
}else{
//记录与上次的间隔时间
System.out.printf("running time:[%d]\n",current-atomicLong.get());
}
//将当前时间保存
atomicLong.set(current);
//模拟超过固定周期时间
mySleep(5);
}, 1,2, TimeUnit.SECONDS);
}
private static void mySleep(int seconds){
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
- 可以看出来,无论你的任务执行多久,在任务执行完毕之后都会延迟一定时间才进入下一周期。
first running [1597659862349]
running time:[7047]
running time:[7002]
running time:[7023]
running time:[7002]
running time:[7003]
5、setContinueExistingPeriodicTasksAfterShutdownPolicy练习
package com.brycen.concurrency03.threadpool;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ScheduledExecutorServiceExample5 {
public static void main(String[] args) {
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(2);
//提交固定周期任务
ScheduledFuture<?> runnableFuture = scheduledThreadPoolExecutor.scheduleAtFixedRate(() -> {
System.out.println("This is runable task running "+Thread.currentThread().getName());
}, 1,2, TimeUnit.SECONDS);
//默认情况关闭线程池后是不允许继续执行固定周期任务的,所有输出false
System.out.println(scheduledThreadPoolExecutor.getContinueExistingPeriodicTasksAfterShutdownPolicy());
//设置为true
scheduledThreadPoolExecutor.setContinueExistingPeriodicTasksAfterShutdownPolicy(true);
//休眠1200毫秒,确保任务被执行
mySleep(1200);
//关闭线程池
scheduledThreadPoolExecutor.shutdown();
//休眠2000毫秒后查看线程池状态
mySleep(2000);
//线程池的状态
System.out.println("isShutdown:"+scheduledThreadPoolExecutor.isShutdown());
System.out.println("isTerminating:"+scheduledThreadPoolExecutor.isTerminating());
System.out.println("isTerminated:"+scheduledThreadPoolExecutor.isTerminated());
}
private static void mySleep(int milliSeconds){
try {
TimeUnit.MILLISECONDS.sleep(milliSeconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
false
This is runable task running pool-1-thread-1
This is runable task running pool-1-thread-1
isShutdown:true
isTerminating:true
isTerminated:false
This is runable task running pool-1-thread-1
This is runable task running pool-1-thread-1
...