Java
Threads
ExecutorService
Concurrency
Delay

Java threads ExecutorService delay between threads

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

ExecutorService does not automatically create a delay "between threads". Threads in a pool run tasks whenever work is available. If you need spacing between task executions, use a ScheduledExecutorService, delay task submission yourself, or put an intentional wait inside the task logic when that matches the real requirement.

Clarify What Kind of Delay You Mean

This question usually hides one of three different goals:

  • delay before a task starts
  • fixed delay between repeated executions of one task
  • throttling so tasks do not all start at once

Those are different concurrency problems, and the right API depends on which one you actually mean.

One-Time Delays With schedule

If you want a task to start after a delay, use ScheduledExecutorService:

java
1import java.util.concurrent.Executors;
2import java.util.concurrent.ScheduledExecutorService;
3import java.util.concurrent.TimeUnit;
4
5public class Main {
6    public static void main(String[] args) {
7        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
8
9        scheduler.schedule(() -> System.out.println("task A"), 1, TimeUnit.SECONDS);
10        scheduler.schedule(() -> System.out.println("task B"), 3, TimeUnit.SECONDS);
11
12        scheduler.shutdown();
13    }
14}

That schedules each task with an explicit initial delay.

Fixed Delay Between Repeated Runs

If the requirement is "run, then wait two seconds, then run again", use scheduleWithFixedDelay:

java
1import java.util.concurrent.Executors;
2import java.util.concurrent.ScheduledExecutorService;
3import java.util.concurrent.TimeUnit;
4
5public class Main {
6    public static void main(String[] args) {
7        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
8
9        scheduler.scheduleWithFixedDelay(() -> {
10            System.out.println("running at " + System.currentTimeMillis());
11        }, 0, 2, TimeUnit.SECONDS);
12    }
13}

Here the delay is measured from the end of one execution to the start of the next.

Fixed Rate Is Different

Java also provides scheduleAtFixedRate, but it means something different. Fixed rate tries to maintain a regular schedule based on start times. Fixed delay waits after each run finishes.

That difference matters when task duration varies. If the task itself sometimes takes longer than expected, fixed delay usually matches "pause between runs" more closely.

Throttling Submissions

If you have many independent tasks and want to stagger them, another option is to delay submission rather than delay worker threads:

java
1import java.util.concurrent.ExecutorService;
2import java.util.concurrent.Executors;
3
4public class Main {
5    public static void main(String[] args) throws InterruptedException {
6        ExecutorService pool = Executors.newFixedThreadPool(3);
7
8        for (int i = 1; i <= 5; i++) {
9            int taskId = i;
10            pool.submit(() -> System.out.println("task " + taskId));
11            Thread.sleep(500);
12        }
13
14        pool.shutdown();
15    }
16}

This is simple, but note that the delay happens in the submitting thread, not inside the executor.

Do Not Sleep Arbitrarily Inside Worker Threads Unless You Mean It

You can put Thread.sleep(...) inside the task body, but that consumes a worker thread while sleeping:

java
1pool.submit(() -> {
2    Thread.sleep(1000);
3    doWork();
4});

That is sometimes acceptable, but it is usually not the best way to schedule delayed work. It reduces effective pool throughput and makes task timing harder to reason about.

Pick the Right Executor Type

Use:

  • 'ExecutorService for plain concurrent execution'
  • 'ScheduledExecutorService for delays and repeated scheduling'

That distinction is the real answer in most cases. If you need timing semantics, reach for the scheduled executor directly instead of trying to simulate it with a normal pool.

Common Pitfalls

The most common mistake is asking for a delay "between threads" when the real requirement is a delay between task starts or repeated task runs. Threads themselves are not the scheduling unit you usually want to control.

Another issue is using Thread.sleep inside worker tasks to fake scheduling. That ties up threads unnecessarily. Developers also often confuse scheduleAtFixedRate with scheduleWithFixedDelay; they sound similar, but the timing rules are different.

Summary

  • 'ExecutorService does not insert delays between worker threads automatically.'
  • Use ScheduledExecutorService when timing is part of the requirement.
  • Use schedule for one-time delayed tasks.
  • Use scheduleWithFixedDelay when you want a pause after each run finishes.
  • Avoid sleeping inside worker threads unless that sleeping time is truly part of the task itself.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.