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

Does ScheduledExecutorService guarantee order when pool size is one?

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

如果我使用该服务安排许多具有相同延迟的任务,在执行期间是否保留安排顺序?

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

Delayed tasks execute no sooner than they are enabled, but without any real-time guarantees about when, after they are enabled, they will commence. Tasks scheduled for exactly the same execution time are enabled in first-in-first-out (FIFO) order of submission.

你也可以在实际中看到这个

    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", 0), 3, TimeUnit.SECONDS);
        e.schedule(delay("delay for 7 second", 0), 7, TimeUnit.SECONDS);
        e.schedule(delay("delay for 2 second", 0), 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 实施应保留顺序:

Tasks scheduled for exactly the same execution time are enabled in first-in-first-out (FIFO) order of submission.

可以用下面的例子来测试实现:

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);
    }
}