ScheduledThreadPoolExecutor
类结构图:
用来处理延时任务或定时任务,采用DelayQueue存储等待的任务,DelayQueue内部封装了一个PriorityQueue,它会根据time的先后时间排序,若time相同则根据sequenceNumber排序;
DelayQueue是一个无界队列;
三种提交任务的方式:
schedule
:此方法提交任务只会执行一次
使用方法:
public class ScheduleThreadPoolTest {
public static void main(String[] args) {
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1);
scheduledThreadPoolExecutor.schedule(() -> {
System.out.println("延迟5s执行");
return 1;
//表示线程开启五秒后执行任务
}, 5000, TimeUnit.MILLISECONDS);
scheduledThreadPoolExecutor.shutdown();
}
}
scheduledAtFixedRate
:此方法可以设置任务执行周期,如果任务执行时间超过了设置的周期时间,会把任务放到DelayQueue中排队,任务执行完成后立即从队列中获取任务继续执行,可以用来实现心跳确认机制。
使用方法:
public class ScheduleThreadPoolTest {
public static void main(String[] args) {
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1);
//每次过5秒,发送一个心跳确认
scheduledThreadPoolExecutor.scheduleAtFixedRate(() -> {
log.info("线程任务开始");
long starttime = System.currentTimeMillis(), nowtime = starttime;
//死循环,当前时间 - 开始时间小于5秒,继续执行
while ((nowtime - starttime) < 5000) {
nowtime = System.currentTimeMillis();
}
//五秒后任务结束
log.info("线程任务结束");
// throw new RuntimeException("unexpected error , stop working");
}, 1000, 2000, TimeUnit.MILLISECONDS);
}
}
scheduledWithFixedDelay
:此方法在scheduledThreadPoolExecutor基础上做了优化,如果任务执行时间超过了设置的周期时间,任务执行完后不会立即拿执行下一个,会等设置的周期时间到了之后再执行任务。它和scheduledAtFixedRate有个共同点,线程如果抛异常,会丢弃该任务重新等待获取新的任务,我们可以利用此特性catch到可能异常让线程重新获取任务,或者在catch中做一些业务处理。
使用方法:
public class ScheduleThreadPoolTest {
public static void main(String[] args) {
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1);
//每次过5秒,发送一个心跳确认
scheduledThreadPoolExecutor.scheduleWithFixedDelay(() -> {
log.info("线程任务开始");
long starttime = System.currentTimeMillis(), nowtime = starttime;
//死循环,当前时间 - 开始时间小于5秒,继续执行
while ((nowtime - starttime) < 5000) {
nowtime = System.currentTimeMillis();
}
//五秒后任务结束
log.info("线程任务结束");
// throw new RuntimeException("unexpected error , stop working");
}, 1000, 2000, TimeUnit.MILLISECONDS);
}
}
Timer定时类
timer和ScheduledThreadPoolExecutor线程池类似,但timer中的thread是一个成员实行,在线程抛异常时会终止线程,而ScheduledThreadPoolExecutor则会阻塞等待获取其他任务。
使用方法:
public class TimerTest {
public static void main(String[] args) {
//定时类
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
log.info("线程任务开始");
long starttime = System.currentTimeMillis(), nowtime = starttime;
//死循环,当前时间 - 开始时间小于5秒,继续执行
while ((nowtime - starttime) < 5000) {
nowtime = System.currentTimeMillis();
}
//五秒后任务结束
log.info("线程任务结束");
// throw new RuntimeException("unexpected error , stop working");
}
}, 1000, 2000);
}
}
DelayedWorkQueue
DelayedWorkQueue是一个基于堆的数据结构,类似于DelayQueue和PriorityQueue。在执行定时任务的时候,每个任务的执行时间都不同,所以DelayedWorkQueue的工作就是按照执行时间的升序来排列。
堆结构介绍:
堆结构是用数组实现的二叉树,数组下标可以表明元素节点的位置,所以省去指针的内存消耗,会根据父节点是否大于左右节点分为最小根堆和最大根堆。
二叉树根节点小于左右节点叫小根堆
二叉树根节点大于左右节点叫大根堆
堆的属性:
1、堆都是满二叉树.因为满二叉树会充分利用数组的内存空间
2、最大堆和最小堆的只需要保证根节点最大或最小,左右节点大小关系无所谓。因为取出、新增、删除操作会对堆进行重新排序,把新的最小或者最大值放到堆顶,如果左右节点要顺序排序的话会扫描整个素组,消耗大量时间。
DelayedWorkQueue属性:
//队列初始容量
private static final int INITIAL_CAPACITY = 16;
//用于记录RunableScheduledFuture任务的数组
private RunnableScheduledFuture<?>[] queue = new RunnableScheduledFuture<?>[INITIAL_CAPACITY];
private final ReentrantLock lock = new ReentrantLock();
//当前队列中任务数,即队列深度
private int size = 0;
//leader线程用于等待队列头部任务,
private Thread leader = null;
//当较新的任务在队列的头部可用时,或者新线程可能需要成为leader,通知其他线程等待
private final Condition available = lock.newCondition();
延迟队列是基于数组实现的,初始容量为16;获取延迟队列中头部节点的线程称为leader,说明leader线程是不断变化的,但leader线程在等待,则其他线程也会等待,直到leader线程获取根节点,且从等待线程中产生新的leader线程。
任务排序方法:
循环的根据key节点与它的父节点来判断,如果key节点的执行时间小于父节点,则将两个节点交换,使执行时间靠前的节点排列在队列的前面。
private void siftUp(int k, RunnableScheduledFuture<?> key) {
//找到父节点的索引
while (k > 0) {
//获取父节点
int parent = (k - 1) >>> 1;
RunnableScheduledFuture<?> e = queue[parent];
//如果key节点的执行时间大于父节点的执行时间,不需要再排序了
if (key.compareTo(e) >= 0)
break;
//如果key.compareTo(e) < 0,说明key节点的执行时间小于父节点的执行时间,需要把父节点移到后面
queue[k] = e;
setIndex(e, k);
//设置索引为k
k = parent;
}
//key设置为排序后的位置中
queue[k] = key;
setIndex(key, k);
}