Java 9 CompletableFuture API Improvements – Delay and Timeout Support

java-9-completablefuture-api-improvements-delay-timeout-feature-image

Java 9 CompletableFuture API Improvements – Delay and Timeout Support

To improve Java Future, Java 8 provides CompletableFuture which can execute some code whenever its ready. In this article, we’re gonna take a look at new Java 9 CompletableFuture API that supports delay and timeout.

Contents

I. Delay


static Executor delayedExecutor(long delay, TimeUnit unit);
static Executor delayedExecutor(long delay, TimeUnit unit, Executor executor);

– The first method, not specify Executor, returns a new Executor that submits a task to the default executor (general purpose pool ForkJoinPool.commonPool()) after delay time.
– The second method, with an Executor as parameter, returns a new Executor that submits a task to that input executor after delay time.

Now see an example to illustrate how the method works. We have a CompletableFuture that will delay its completion by 3 seconds. The code could be:


future.completeAsync(supplier, CompletableFuture.delayedExecutor(3, TimeUnit.SECONDS))
		.thenAccept(result -> System.out.println("accept: " + result));

// other statements

The program runs ‘other statements’ first. 3 seconds later, it call get() method of supplier and pass the result to thenAccept().

II. Timeout

1. orTimeout()


CompletableFuture orTimeout(long timeout, TimeUnit unit);

The method exceptionally completes current CompletableFuture by throwing a TimeoutException if not otherwise completed before the timeout.

For example, we have a doWork() method that takes 5 seconds to return a CompletableFuture. But we set TIMEOUT only 3 seconds.


// TIMEOUT = 3;
// doWork() takes 5 seconds to finish

CompletableFuture future = 
		doWork("JavaSampleApproach")
		.orTimeout(TIMEOUT, TimeUnit.SECONDS)
		.whenComplete((result, error) -> {
			if (error == null) {
				System.out.println("The result is: " + result);
			} else {
				System.out.println("Sorry, timeout in " + TIMEOUT + " seconds");
			}
		});

Run the code above, we get result that prints out:
Sorry, timeout in 3 seconds
and throw a java.util.concurrent.TimeoutException.

Java 9 new CompletableFuture API also gives us another method to handle the case when timeout: completeOnTimeout().

2. completeOnTimeout()


CompletableFuture completeOnTimeout(T value, long timeout, TimeUnit unit);

The method completes current CompletableFuture by input value if not otherwise completed before the timeout.

Like the example above, we have a doWork() method that takes 5 seconds to return a CompletableFuture. But we set TIMEOUT only 3 seconds. We also specify the string value JavaTechnology in case the time 3 seconds is out and doWork() hasn’t done its operation yet.


// TIMEOUT = 3;
// doWork() takes 5 seconds to finish

CompletableFuture future = 
		doWork("JavaSampleApproach")
		.completeOnTimeout("JavaTechnology", TIMEOUT, TimeUnit.SECONDS)
		.whenComplete((result, error) -> {
			if (error == null) {
				System.out.println("The result is: " + result);
			} else {
				// this statement will never run.
				System.out.println("Sorry, timeout in " + TIMEOUT + " seconds.");
			}
		});

Run the code above, we get result that prints out:
The result is: JavaTechnology

III. SOURCE CODE

1. DelayedExecutorTest.java


package com.javasampleapproach.java9completablefuture;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

public class DelayedExecutorTest {

	public static void main(String[] args) throws InterruptedException, ExecutionException {

		CompletableFuture future = new CompletableFuture<>();
		future.completeAsync(() -> {
				try {
					System.out.println("inside future: processing data...");

					return "grokonez.com";
				} catch (Throwable e) {
					return "not detected";
				}
		}, CompletableFuture.delayedExecutor(3, TimeUnit.SECONDS))
		.thenAccept(result -> System.out.println("accept: " + result));

		// other statements
		for (int i = 1; i <= 5; i++) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("running outside... " + i + " s");
		}

	}
}

Result:


running outside... 1 s
running outside... 2 s
running outside... 3 s
inside future: processing data...
accept: grokonez.com
running outside... 4 s
running outside... 5 s

2. OrTimeOutTest.java


package com.javasampleapproach.java9completablefuture;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

public class OrTimeOutTest {

	private static final int TIMEOUT = 3;

	public static void main(String[] args) throws InterruptedException, ExecutionException {

		CompletableFuture future = 
				doWork("JavaSampleApproach")
				.orTimeout(TIMEOUT, TimeUnit.SECONDS)
				.whenComplete((result, error) -> {
					if (error == null) {
						System.out.println("The result is: " + result);
					} else {
						System.out.println("Sorry, timeout in " + TIMEOUT + " seconds.");
					}
				});

		String content;
		content = future.get();
		System.out.println("Result >> " + content);

	}

	private static CompletableFuture doWork(String s) {

		return CompletableFuture.supplyAsync(() -> {
			for (int i = 1; i <= 5; i++) {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("running inside doWork()... " + i + " s");
			}
			return s + ".com";
		});
	}

}

Result:


running inside doWork()... 1 s
running inside doWork()... 2 s
running inside doWork()... 3 s
Sorry, timeout in 3 seconds.
Exception in thread "main" java.util.concurrent.ExecutionException: java.util.concurrent.TimeoutException
	at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:395)
	at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1999)
	at com.javasampleapproach.java9completablefuture.OrTimeOutTest.main(OrTimeOutTest.java:26)
Caused by: java.util.concurrent.TimeoutException
	at java.base/java.util.concurrent.CompletableFuture$Timeout.run(CompletableFuture.java:2792)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:514)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:299)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1161)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:844)

If we change TIMEOUT to 6


running inside doWork()... 1 s
running inside doWork()... 2 s
running inside doWork()... 3 s
running inside doWork()... 4 s
running inside doWork()... 5 s
The result is: grokonez.com
Result >> grokonez.com

3. CompleteOnTimeoutTest.java


package com.javasampleapproach.java9completablefuture;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

public class CompleteOnTimeoutTest {

	private static final int TIMEOUT = 3;

	public static void main(String[] args) throws InterruptedException, ExecutionException {

		CompletableFuture future = 
				doWork("JavaSampleApproach")
				.completeOnTimeout("JavaTechnology", TIMEOUT, TimeUnit.SECONDS).whenComplete((result, error) -> {
					if (error == null) {
						System.out.println("The result is: " + result);
					} else {
						// this statement will never run.
						System.out.println("Sorry, timeout in " + TIMEOUT + " seconds.");
					}
				});

		String content = future.get();
		System.out.println("Result >> " + content);
	}

	private static CompletableFuture doWork(String s) {

		return CompletableFuture.supplyAsync(() -> {
			for (int i = 1; i <= 5; i++) {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("running inside doWork()... " + i + " s");
			}
			return s + ".com";
		});
	}

}

Result:


running inside doWork()... 1 s
running inside doWork()... 2 s
running inside doWork()... 3 s
The result is: JavaTechnology
Result >> JavaTechnology

If we change TIMEOUT to 6


running inside doWork()... 1 s
running inside doWork()... 2 s
running inside doWork()... 3 s
running inside doWork()... 4 s
running inside doWork()... 5 s
The result is: grokonez.com
Result >> grokonez.com


By grokonez | March 28, 2017.

Last updated on April 3, 2021.



Related Posts


2 thoughts on “Java 9 CompletableFuture API Improvements – Delay and Timeout Support”

Got Something To Say:

Your email address will not be published. Required fields are marked *

*