Java
Concurrency
Runnable
Timeout
Multithreading

How can I implement a Runnable with timeout?

Interview Questions practice on Codemia

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

Browse interview questions

Introduction

In Java, timeout behavior is usually implemented around a Runnable, not inside the Runnable interface itself. The standard pattern is to submit the task to an ExecutorService, wait through a Future, and cancel the task if it runs past the deadline.

Wrap the Runnable in an Executor Task

A Runnable has no built-in timeout API, so you need an execution framework that can track completion. ExecutorService provides that by returning a Future from submit.

java
1import java.util.concurrent.ExecutorService;
2import java.util.concurrent.Executors;
3import java.util.concurrent.Future;
4import java.util.concurrent.TimeUnit;
5import java.util.concurrent.TimeoutException;
6
7public class RunnableTimeoutDemo {
8    public static void main(String[] args) {
9        ExecutorService executor = Executors.newSingleThreadExecutor();
10
11        Runnable task = () -> {
12            try {
13                Thread.sleep(5000);
14                System.out.println("Task finished");
15            } catch (InterruptedException e) {
16                Thread.currentThread().interrupt();
17                System.out.println("Task interrupted");
18            }
19        };
20
21        Future<?> future = executor.submit(task);
22
23        try {
24            future.get(2, TimeUnit.SECONDS);
25            System.out.println("Completed within timeout");
26        } catch (TimeoutException e) {
27            future.cancel(true);
28            System.out.println("Timed out");
29        } catch (Exception e) {
30            e.printStackTrace();
31        } finally {
32            executor.shutdown();
33        }
34    }
35}

This is the usual production answer because it keeps the task logic separate from the timeout policy. The Runnable does the work. The caller decides how long it is allowed to run.

Understand What Cancellation Actually Means

future.cancel(true) does not forcibly kill code at an arbitrary instruction. It sends an interruption request to the worker thread. The task must cooperate by either reacting to InterruptedException or checking the interrupted flag.

java
1public class CooperativeTask implements Runnable {
2    @Override
3    public void run() {
4        while (!Thread.currentThread().isInterrupted()) {
5            doSmallUnitOfWork();
6        }
7        System.out.println("Stopping cleanly");
8    }
9
10    private void doSmallUnitOfWork() {
11        try {
12            Thread.sleep(100);
13        } catch (InterruptedException e) {
14            Thread.currentThread().interrupt();
15        }
16    }
17}

That cooperative model is important. If the task ignores interruption or blocks in an API that does not respond to interruption, the timeout may expire while the underlying work continues.

Use Callable When a Result Matters

If the caller needs a return value, switch to Callable instead of trying to mutate shared state from a Runnable. The timeout logic stays the same.

java
1import java.util.concurrent.Callable;
2import java.util.concurrent.ExecutionException;
3import java.util.concurrent.ExecutorService;
4import java.util.concurrent.Executors;
5import java.util.concurrent.Future;
6import java.util.concurrent.TimeUnit;
7import java.util.concurrent.TimeoutException;
8
9public class CallableTimeoutDemo {
10    public static void main(String[] args) {
11        ExecutorService executor = Executors.newSingleThreadExecutor();
12
13        Callable<String> task = () -> {
14            Thread.sleep(1000);
15            return "done";
16        };
17
18        Future<String> future = executor.submit(task);
19
20        try {
21            String result = future.get(2, TimeUnit.SECONDS);
22            System.out.println(result);
23        } catch (TimeoutException e) {
24            future.cancel(true);
25        } catch (InterruptedException | ExecutionException e) {
26            e.printStackTrace();
27        } finally {
28            executor.shutdown();
29        }
30    }
31}

This is often a cleaner design because it avoids extra shared variables and makes completion semantics explicit.

Consider a Scheduled Cancellation for Reuse

If many parts of the application need the same timeout behavior, a helper method can hide the boilerplate. One common variation is to submit the work and schedule a cancellation request separately.

java
1import java.util.concurrent.ExecutorService;
2import java.util.concurrent.Executors;
3import java.util.concurrent.Future;
4import java.util.concurrent.ScheduledExecutorService;
5import java.util.concurrent.TimeUnit;
6
7public class TimeoutHelper {
8    public static Future<?> runWithTimeout(Runnable task, long timeoutMillis) {
9        ExecutorService workerPool = Executors.newSingleThreadExecutor();
10        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
11
12        Future<?> future = workerPool.submit(task);
13        scheduler.schedule(() -> future.cancel(true), timeoutMillis, TimeUnit.MILLISECONDS);
14
15        workerPool.shutdown();
16        scheduler.shutdown();
17        return future;
18    }
19}

The same rule still applies: cancellation is only effective if the task cooperates.

Common Pitfalls

  • Assuming a timeout can force-stop arbitrary Java code immediately.
  • Ignoring interruption inside the task and then wondering why cancellation appears ineffective.
  • Using raw Thread management instead of the higher-level executor APIs.
  • Choosing Runnable when the caller really needs a computed result and should use Callable.
  • Forgetting to shut down the executor, which can leave non-daemon threads alive after the work is done.

Summary

  • Implement timeouts with ExecutorService and Future, not by extending Runnable.
  • Use future.get(timeout, unit) to wait for completion up to a deadline.
  • Call future.cancel(true) on timeout and make the task cooperate with interruption.
  • Prefer Callable when the task should return a value.
  • Treat timeout handling as cooperative cancellation, not forced termination.

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.