# Java Fix a TimeoutException from Future.get()

> Optimize the task so it finishes sooner, increase the timeout passed to Future.get(), or use CompletableFuture.completeOnTimeout() for a graceful fallback

**URL:** https://sentry.io/answers/resolving-java-util-concurrent-timeoutexception-errors-in-java/

---

## The Problem

In Java, you may run into a `java.util.concurrent.TimeoutException` error when using the `java.util.concurrent` package:

```
Task timed out: null                                                                                              
java.util.concurrent.TimeoutException                                                                             
        at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:204)
        at TimeoutExceptionExample.main(TimeoutExceptionExample.java:17)
```

## The Solution

You can resolve a `java.util.concurrent.TimeoutException` error by ensuring the task causing the error does not exceed its timeout limit or by returning a default value when the limit is reached.

The `TimeoutException` indicates that the error was thrown by the `get` method of `FutureTask`. Specifically, it was thrown by the overloaded version of the `get` method that accepts a timeout parameter, as shown in the following line from the stack trace:

```
at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:204)
```

This design ensures that the task can be controlled and prevented from blocking indefinitely. However, if the specified timeout is too short or the task encounters delays, a `TimeoutException` error occurs. 

Let's use the following example to reproduce and resolve the issue:

```java
import java.util.concurrent.*;

public class TimeoutExceptionExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        Future<Integer> future = executorService.submit(() -> {
            int sum = 0;
            for (int i = 1; i <= 1000; i++) {   // Add numbers from 1 to 1000
                Thread.sleep(1);                // Simulate a delay for each addition
                sum += i;
            }
            return sum;
        });

        try {
            Integer result = future.get(1, TimeUnit.SECONDS);
            System.out.println("Sum: " + result);
        } catch (TimeoutException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown();
        }
    }
}
```

This code calculates the sum of numbers from 1 to 1000. It simulates a delay for each addition, which causes the task to exceed the timeout limit.

### Optimize the Task

First things first: Get rid of any inefficiencies.

The delay in the loop (`Thread.sleep(1)`) causes the task to take longer than expected. Remove the delay to optimize the code:

```java
import java.util.concurrent.*;

public class OptimizedTimeoutExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        Future<Integer> future = executorService.submit(() -> {
            int sum = 0;
            for (int i = 1; i <= 1000; i++) {
                sum += i; // Perform addition without delay.
            }
            return sum;
        });

        try {
            Integer result = future.get(1, TimeUnit.SECONDS); 
            System.out.println("Sum: " + result);
        } catch (TimeoutException e) {
            System.err.println("Task timed out: " + e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown();
        }
    }
}
```

This prints:

```
Sum: 500500   
```

### Increase the Timeout

When optimization isn't an option, give the task more time to complete:

```java
import java.util.concurrent.*;

public class TimeoutExceptionExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        Future<Integer> future = executorService.submit(() -> {
            int sum = 0;
            for (int i = 1; i <= 1000; i++) {
                sum += i; 
                Thread.sleep(1); 
            }
            return sum;
        });

        try {
            Integer result = future.get(3, TimeUnit.SECONDS); // Extend the timeout limit to 3 seconds
            System.out.println("Sum: " + result);
        } catch (TimeoutException e) {
            System.err.println("Task timed out: " + e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown();
        }
    }
}
```

This prints:

```
Sum: 500500
```

### Handle the Timeout Gracefully

If extending the timeout still doesn't work, you can use a fallback mechanism to handle the error.

This example returns a default value when the task times out:

```java
import java.util.concurrent.*;

public class FallbackTimeoutExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        Future<Integer> future = executorService.submit(() -> {
            int sum = 0;
            for (int i = 1; i <= 1000000; i++) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                sum += i;
            }
            return sum;
        });

        try {
            Integer result = future.get(1, TimeUnit.SECONDS);
            System.out.println("Sum: " + result);
        } catch (TimeoutException e) {
            System.err.println("Task timed out. Returning default value.");
            System.out.println("Sum: 0 (Default value)");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown();
        }
    }
}
```

This prints:

```
Task timed out. Returning default value.
Sum: 0 (Default value)
```

### Use Asynchronous Processing for the Timeout

Let's get fancy and use the `CompletableFuture` method to define a default value in case of a timeout:

```java
import java.util.concurrent.*;

public class CompletableFutureExample {
    public static void main(String[] args) {
        // This runs the task asynchronously.
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            int sum = 0; 
            for (int i = 1; i <= 1000000; i++) {
                sum += i;
                try {
                    Thread.sleep(1); // Simulate a delay
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
            return sum;
        });

        // If the task does not complete within 1 second, return the default value 1784293664.
        Integer result = future.completeOnTimeout(1784293664, 1, TimeUnit.SECONDS).join(); 
        
        System.out.println("Sum: " + result);
    }
}
```

This prints:

```
Sum: 1784293664
```

## Note

The `Future` interface provides two versions of the `get` method:

- `get()` waits indefinitely for the task to complete.
- `get(long timeout, TimeUnit unit)` waits up to the specified timeout for the task to complete, throwing a `TimeoutException` if the timeout is exceeded.

While using the parameterless `get()` avoids the TimeoutException` error, it can result in the program waiting indefinitely, which is not ideal.

## Further Reading

- The [Java `Future`](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Future.html) documentation
- The [Java `CompletableFuture` class](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html) documentation
- The [Java Concurrency Tutorial](https://docs.oracle.com/javase/tutorial/essential/concurrency/)

---

*Source: [sentry.io/answers/resolving-java-util-concurrent-timeoutexception-errors-in-java/](https://sentry.io/answers/resolving-java-util-concurrent-timeoutexception-errors-in-java/)*
