当池大小为 1 时,ScheduledExecutorService 是否保证顺序?

问题描述

我有一个 scheduledexecutorservice,其池大小为 1 个线程。

如果我使用该服务以相同的延迟调度多个任务,在执行期间是否保留调度顺序?

解决方法

是的,订单被保留。来自 javadocs

延迟任务在启用后立即执行,但没有任何关于启用后何时开始的实时保证。计划执行时间完全相同的任务按提交的先进先出 (FIFO) 顺序启用。

你也可以看到这一点

    public static void main(String args[]) {
        ScheduledExecutorService e = Executors.newScheduledThreadPool(1);
        
        e.schedule(delay("delay for 1 second",10),1,TimeUnit.SECONDS);
        e.schedule(delay("delay for 5 second",0),5,TimeUnit.SECONDS);
        e.schedule(delay("delay for 3 second",3,TimeUnit.SECONDS);
        e.schedule(delay("delay for 7 second",7,TimeUnit.SECONDS);
        e.schedule(delay("delay for 2 second",2,TimeUnit.SECONDS);
    }
    
    private static Runnable delay(String message,int initialDelay) {
        return () -> {
            Thread.sleep(initialDelay);
            System.out.println(message);
        };
    }

印刷品

delay for 1 second
delay for 2 second
delay for 3 second
delay for 5 second
delay for 7 second
,

是的,只要使用的调度器实现将遵循接口规范。例如,new ScheduledThreadPoolExecutor(1) 将使用将保留顺序的 DelayedWorkQueue

根据 javadoc,所有 ScheduledExecutorService 实现都应保持顺序:

计划执行时间完全相同的任务按提交的先进先出 (FIFO) 顺序启用。

可以使用下面的示例测试实现:

import com.google.code.tempusfugit.concurrency.IntermittentTestRunner;
import com.google.code.tempusfugit.concurrency.annotations.Intermittent;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

import static org.assertj.core.api.Assertions.assertThat;


@RunWith(IntermittentTestRunner.class)
public class ScheduledExecutorServiceTest {
    @Test
    @Intermittent(repetition = 20)
    public void preservesOrderOfTasksScheduledWithSameDelay() throws InterruptedException {
        ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1);

        AtomicInteger atomicInteger = new AtomicInteger(0);
        int numTasks = 1_000;
        CountDownLatch countDownLatch = new CountDownLatch(numTasks);
        for (int i = 0; i < numTasks; i++) {
            int finalI = i;
            scheduledExecutorService.schedule(() -> {
                atomicInteger.compareAndSet(finalI,finalI + 1);
                countDownLatch.countDown();
            },10,TimeUnit.MILLISECONDS);
        }
        countDownLatch.await();

        assertThat(atomicInteger.get()).isEqualTo(numTasks);
    }
}