ScheduledFutureTask



//Netty当中的异步任务,把逻辑封装到此类异步完成后获取结果
final class ScheduledFutureTask<V> extends PromiseTask<V> implements ScheduledFuture<V>, PriorityQueueNode {
    //任务ID 自增,唯一
    private static final AtomicLong nextTaskId = new AtomicLong();
    //启动纳秒
    private static final long START_TIME = System.nanoTime();

    //当前纳秒减去启动纳秒,相当于一个自增的时间差值
    static long nanoTime() {
        return System.nanoTime() - START_TIME;
    }

    //给定延迟时间,计算到期时间
    static long deadlineNanos(long delay) {
        long deadlineNanos = nanoTime() + delay;
        // Guard against overflow 防止溢出
        return deadlineNanos < 0 ? Long.MAX_VALUE : deadlineNanos;
    }

    //成员变量-自增ID
    private final long id = nextTaskId.getAndIncrement();

    //到期时间
    private long deadlineNanos;
    
    private final long periodNanos;

    //任务在队列中的位置
    private int queueIndex = INDEX_NOT_IN_QUEUE;

    ScheduledFutureTask(
            AbstractScheduledEventExecutor executor,
            Callable<V> callable, long nanoTime, long period) {

        //调用父类构造参数
        //父类源码分析在https://blog.csdn.net/nimasike/article/details/102024585
        super(executor, callable);
        if (period == 0) {
            throw new IllegalArgumentException("period: 0 (expected: != 0)");
        }
        deadlineNanos = nanoTime;
        periodNanos = period;
    }

    //deadlineNanos = 创建此类时的(当前时间) -差值(固定值) + delay 目的让数变小一点
    //nanoTime()= 系统调用当前时间 - 差值(固定值)
    //deadlineNanos - nanoTime() = 创建此类时的(当前时间) -差值(固定值) + delay - (系统调用当前时间 - 差值(固定值))
    //创建此类时的(当前时间) + delay - 系统调用当前时间
    //值随着时间的推移会小于0,则意味着到期了。
    public long delayNanos() {
        return Math.max(0, deadlineNanos() - nanoTime());
    }

    //计算到期时间,使用currentTimeNanos参数而不是系统时间,原理同上
    public long delayNanos(long currentTimeNanos) {
        return Math.max(0, deadlineNanos() - (currentTimeNanos - START_TIME));
    }

    //根据时间单元获取到期时间
    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(delayNanos(), TimeUnit.NANOSECONDS);
    }

    //比较到期时间大小
    @Override
    public int compareTo(Delayed o) {
        if (this == o) {
            return 0;
        }

        ScheduledFutureTask<?> that = (ScheduledFutureTask<?>) o;
        long d = deadlineNanos() - that.deadlineNanos();
        if (d < 0) {
            return -1;
        } else if (d > 0) {
            return 1;
        } else if (id < that.id) {
            return -1;
        } else if (id == that.id) {
            throw new Error();
        } else {
            return 1;
        }
    }

    @Override
    public void run() {
        //必须要在executor的线程中执行
        assert executor().inEventLoop();
        try {
            //如果为0执行一次获取结果设置成功状态
            if (periodNanos == 0) {
                if (setUncancellableInternal()) {
                    V result = task.call();
                    setSuccessInternal(result);
                }
            } else {
                // check if is done as it may was cancelled
                //检查是否取消
                if (!isCancelled()) {
                    //调用具体任务逻辑
                    task.call();
                    //判断executor是否结束
                    if (!executor().isShutdown()) {

                        long p = periodNanos;
                        //以固定速率重复, 打个比方,说白了就是不管任务具体执行多久,任务总是在1秒,5秒,10秒,15秒时刻运行
                        //也就是将在 initialDelay 后开始执行,然后在 initialDelay+p 后执行,接着在 initialDelay + 2 * p 后执行,依此类推。
                        //有一个特殊情况 如果此任务的任何一个执行要花费比其周期更长的时间,则将推迟后续执行,但不会同时执行。 
                        //比如任务间隔p是5秒,但是任务执行了10秒,那么不会在第5秒的时刻同时执行这个任务,会在10秒结束后立即执行这个任务。
                        if (p > 0) {
                            deadlineNanos += p;
                        } else {
                            //以固定速率延迟
                            //它是在上一次任务完成后,延迟多久再次运行,比如上次任务1秒,5秒执行。
                            //假设在5秒执行的任务运行了25秒,此时到达30秒时刻,那么任务再35秒才会再次运行
                            deadlineNanos = nanoTime() - p;
                        }

                        //检查是否取消
                        if (!isCancelled()) {
                            // scheduledTaskQueue can never be null as we lazy init it before submit the task!
                            Queue<ScheduledFutureTask<?>> scheduledTaskQueue =
                                    ((AbstractScheduledEventExecutor) executor()).scheduledTaskQueue;
                            assert scheduledTaskQueue != null;
                            //把当前任务加入任务队列
                            scheduledTaskQueue.add(this);
                        }
                    }
                }
            }
        } catch (Throwable cause) {
            //异常设置失败状态
            setFailureInternal(cause);
        }
    }

    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
        boolean canceled = super.cancel(mayInterruptIfRunning);
        if (canceled) {
            ((AbstractScheduledEventExecutor) executor()).removeScheduled(this);
        }
        return canceled;
    }

    boolean cancelWithoutRemove(boolean mayInterruptIfRunning) {
        return super.cancel(mayInterruptIfRunning);
    }

    //获取在队列中的索引位置
    @Override
    public int priorityQueueIndex(DefaultPriorityQueue<?> queue) {
        return queueIndex;
    }

    //设置在队列中的索引位置
    @Override
    public void priorityQueueIndex(DefaultPriorityQueue<?> queue, int i) {
        queueIndex = i;
    }
}

 

用java的类测试scheduleAtFixedRate方法,当间隔时间小于任务的执行时间,任务会每隔5秒运行。

比如0秒时刻,5秒时刻,10秒时刻。

ScheduledThreadPoolExecutor sExecutor = new ScheduledThreadPoolExecutor(30);
		sExecutor.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread() + "task is run 10 second" + new Date());
				try {
					Thread.sleep(3000);
					System.out.println(Thread.currentThread() + "task is run 10 second end" + new Date());
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, 0, 5, TimeUnit.SECONDS);

Thread[pool-1-thread-1,5,main]task is run 10 secondThu Oct 10 15:17:19 CST 2019   任务开始时间
Thread[pool-1-thread-1,5,main]task is run 10 second endThu Oct 10 15:17:22 CST 2019  任务结束时间,执行了3秒
Thread[pool-1-thread-1,5,main]task is run 10 secondThu Oct 10 15:17:24 CST 2019   第二个任务开始时间,和第一个差5秒没问题。

 

如果任务间隔小于任务的实际执行时间,那么已实际执行时间为准,不会并发启动第二个任务。

ScheduledThreadPoolExecutor sExecutor = new ScheduledThreadPoolExecutor(30);
		sExecutor.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread() + "task is run 10 second" + new Date());
				try {
					Thread.sleep(10000);
					System.out.println(Thread.currentThread() + "task is run 10 second end" + new Date());
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, 0, 5, TimeUnit.SECONDS);
	}

Thread[pool-1-thread-1,5,main]task is run 10 secondThu Oct 10 15:16:06 CST 2019  任务开始时间
Thread[pool-1-thread-1,5,main]task is run 10 second endThu Oct 10 15:16:16 CST 2019 任务结束时间,执行了10秒。
Thread[pool-1-thread-1,5,main]task is run 10 secondThu Oct 10 15:16:16 CST 2019 第二个任务开始时间,与第一个间隔了10秒,而不是在第5秒时启动第二个线程运行。

 

scheduleWithFixedDelay是再任务结束后计算间隔时间,不管前一个任务允许多久,比如允许了10秒,那么它是在第一个任务执行后5秒执行第二个任务。

ScheduledThreadPoolExecutor sExecutor = new ScheduledThreadPoolExecutor(30);
		sExecutor.scheduleWithFixedDelay(new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread() + "task is run 10 second" + new Date());
				try {
					Thread.sleep(3000);
					System.out.println(Thread.currentThread() + "task is run 10 second end" + new Date());
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, 0, 5, TimeUnit.SECONDS);

Thread[pool-1-thread-1,5,main]task is run 10 secondThu Oct 10 15:19:11 CST 2019   第一个任务执行时间
Thread[pool-1-thread-1,5,main]task is run 10 second endThu Oct 10 15:19:14 CST 2019  第一个任务执行结束时间,允许了3秒
Thread[pool-1-thread-1,5,main]task is run 10 secondThu Oct 10 15:19:19 CST 2019  第二个任务允许时间实在结束时间上+5秒。