Java FutureTask Example
1. Introduction
Java provides the java.util.concurrent package since 1.5 to support parallel execution. It improves the performance when dealing with a time consuming task. The java.util.concurrent.FutureTask class provides a base implementation of the java.util.concurrent.Future interface. The following class diagram outlines FutureTask and its relationship with Callable, Runnable, Future, Executor, and ExecutorService.

In this example, I will demonstrate the following:
- How to create a FutureTask object from a Callable Task
- How to create a FutureTask object from a Runable Task
- How to run tasks parallel
- How to cancel a FutureTask
2. Technologies Used
The example code in this article was built and run using:
- Java 11
- Maven 3.3.9
- Eclipse Oxygen
3. Maven Project
3.1 Dependencies
No any dependencies needed.
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.jcg.example</groupId> <artifactId>FutureTask-example-code</artifactId> <packaging>jar</packaging> <version>1.0</version> <name>FutureTask Example Code</name> <build> <sourceDirectory>src</sourceDirectory> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <release>11</release> </configuration> </plugin> </plugins> </build> </project>
3.2 Count Service
In this step, I will create a CountService which has one method – countNumberOfDivisble(long firstNumber, long lastNumber, long divisor) method. It returns the number of multiples of divisor between firstNumber and lastNumber.
CountService.java
package org.jcg.zheng;
public class CountService {
/**
* Count number of divisible.
*
* Returns the count of numbers that can be divided by the divisor without
* remainder.
*
* @param firstNumber the start number
* @param lastNumber the finish number
* @param divisor the divisor
* @return the count of these numbers which can be divisible by the divisor from
* firstNumber to the lastNumber
*/
public long countNumberOfDivisible(long firstNumber, long lastNumber, long divisor) {
long count = 0;
for (long i = firstNumber; i <= lastNumber; i++) {
if (i % divisor == 0) {
count++;
}
}
return count;
}
}
3.3 Count Task Data
In this step, I will create a CountTaskData which defines the following data members:
beginNumber– for beginning number.endNumber– for ending number.DIVISOR– constant with value of 3.MAX_NUMBER– constant with value of 30,000,000,001.createTwoTasks()– create twoCountTaskDataobjects. The first starts from 0 to the middle of the max number. The second starts from the middle and ends at the max number. I will use it later to count the multiples of 3 from 0 to 30,000,000,001 concurrently.
CountTaskData.java
package org.jcg.zheng;
import java.util.ArrayList;
import java.util.List;
public class CountTaskData {
// DIVISOR to be used in calculation
public static final long DIVISOR = 3;
// Maximum number to check
public static final long MAX_NUMBER = 3000000000l;
public static List<CountTaskData> createTwoTasks() {
List<CountTaskData> tasks = new ArrayList<>();
tasks.add(new CountTaskData(0, MAX_NUMBER / 2));
tasks.add(new CountTaskData(MAX_NUMBER / 2 + 1, MAX_NUMBER));
return tasks;
}
private long beginNumber;
private long endNumber;
public CountTaskData(long beginNumber, long endNumber) {
super();
this.beginNumber = beginNumber;
this.endNumber = endNumber;
}
public long getBeginNumber() {
return beginNumber;
}
public long getEndNumber() {
return endNumber;
}
}
3.4 Callable Count Task
In this step, I will create a CallableCountTask class which implements Callable and returns a Long value.
CallableCountTask– constructor to create an object.call()– invokescountService.countNumerOfDivisible() and returns the counts.
CallableCountTask.java
package org.jcg.zheng.concurrent;
import java.util.concurrent.Callable;
import org.jcg.zheng.CountService;
public class CallableCountTask implements Callable<Long> {
private CountService cutService = new CountService();
private long divisor;
private long first;
private long last;
public CallableCountTask(long first, long last, long divisor) {
this.first = first;
this.last = last;
this.divisor = divisor;
}
@Override
public Long call() throws Exception {
System.out.println(Thread.currentThread().getName() + " call starts.");
long ret = countService.countNumberOfDivisible(first, last, divisor);
System.out.println(Thread.currentThread().getName() + " call ends.");
return ret;
}
}
3.5 RunnableTask
In this step, I will create a RunnableTask class which implements Runnable and doesn’t return anything.
run()– sleeps for a given period, catchesjava.lang.InterruptedExceptionand prints out a message.
RunnableTask.java
package org.jcg.zheng.concurrent;
public class RunnableTask implements Runnable {
// use this to illustrate a long running task
private long sleepMills;
public RunnableTask(long sleepMills) {
super();
this.sleepMills = sleepMills;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " run starts.");
Thread.sleep(sleepMills);
System.out.println(Thread.currentThread().getName() + " run ends.");
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " interrupted.");
}
}
}
4. Demo
4.1 Sequential Execution Demo
In this step, I will create a SequentialExecutionDemo class which finds the number of multiples of 3 between 0 and 30,000,000,001.
SequentialExecutionDemo.java
package org.jcg.zheng.concurrent;
import java.time.Duration;
import java.time.Instant;
import org.jcg.zheng.CountService;
import org.jcg.zheng.CountTaskData;
public class SequentialExecutionDemo {
public static void main(String[] args) {
// Completed in 46805 ms.
Instant begin = Instant.now();
long counts = (new CountService()).countNumberOfDivisible(0, CountTaskData.MAX_NUMBER, CountTaskData.DIVISOR);
Instant end = Instant.now();
System.out.println("Result: " + counts + " time=" + Duration.between(begin, end).toMillis() + " ms.");
}
}
Execute it as a Java application and capture the output here.
C:\MaryZheng\Workspaces\jcg-FutureTask-example\target\classes>java org.jcg.zheng.concurrent.SequentialExecutionDemo Result: 1000000001 time=47471 ms.
As you seen here, it took about 47 seconds to complete.
4.2 Parallel Execution Demo
In this step, I will create a ParallelExecutionDemo class which finds the number of multiples of 3 between 0 and 30,000,000,001 with two parallel tasks. Here are the main steps:
- Create a two-thread pool with java.util.concurrent.Executors.
- Create two
FutureTaskobjects withCallableCountTask. - Submit or execute the
FutureTask. - Get the
FutureTaskresult. - Combine two
FutureTask‘s results.
In this step, I will demonstrate with three different methods:
executeViaFutureTask() – creates twoFutureTasks withCallableCountTask. InvokingExecutorService‘sexecute() method.submitViaFutureTask() — creates twoFutureTaskswithCallableCountTask. Invoking ExecutorService’ssubmit()method.submitViaCallableTask() – Using ExecutorService’s’s submit() method and usesFutureto get the result. This is to show the difference betweenFutureandFutureTask.
ParallelExecutionDemo.java
package org.jcg.zheng.concurrent;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import org.jcg.zheng.CountTaskData;
public class ParallelExecutionDemo {
public static void main(String[] args) {
Instant begin = Instant.now();
System.out.println("Starting parallel execution ....");
// Create a new ExecutorService with 2 thread to execute and store the Futures
ExecutorService executor = Executors.newFixedThreadPool(2);
List<FutureTask<Long>> tasks = new ArrayList<>();
long resultFuture = 0;
switch (args[0]) {
case "executeViaFutureTask":
resultFuture = executeViaFutureTask(executor, tasks);
break;
case "submitViaFutureTask":
resultFuture = submitViaFutureTask(executor, tasks);
break;
case "submitViaCallableTask":
resultFuture = submitViaCallableTask(executor);
break;
}
// Shutdown the ExecutorService
executor.shutdown();
Instant end = Instant.now();
System.out.println(
"Result (Future): " + resultFuture + " time= " + Duration.between(begin, end).toMillis() + " ms");
}
/**
* Result (Future): 1000000001 time= 45612 ms Result (Future): 1000000001 time=
* 35592 ms
*/
private static long executeViaFutureTask(ExecutorService executor, List<FutureTask<Long>> tasks) {
for (CountTaskData td : CountTaskData.createTwoTasks()) {
FutureTask<Long> futureTask = new FutureTask<>(
new CallableCountTask(td.getBeginNumber(), td.getEndNumber(), CountTaskData.DIVISOR));
tasks.add(futureTask);
executor.execute(futureTask);
}
return getConcurrentResult(tasks);
}
/**
* Result (Future): 1000000001 time= 33320 ms
*/
private static long submitViaFutureTask(ExecutorService executor, List<FutureTask<Long>> tasks) {
for (CountTaskData td : CountTaskData.createTwoTasks()) {
FutureTask<Long> futureTask = new FutureTask<>(
new CallableCountTask(td.getBeginNumber(), td.getEndNumber(), CountTaskData.DIVISOR));
tasks.add(futureTask);
executor.submit(futureTask);
}
return getConcurrentResult(tasks);
}
private static long getConcurrentResult(List<FutureTask<Long>> tasks) {
long resultFuture = 0;
// Wait until all results are available and combine them at the same time
for (FutureTask<Long> futureTask : tasks) {
try {
resultFuture += futureTask.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
return resultFuture;
}
/**
* Result (Future): 1000000001 time= 32490 ms
*/
private static long submitViaCallableTask(ExecutorService executor) {
long resultFuture = 0;
List<Future<Long>> taskList = new ArrayList<>();
for (CountTaskData td : CountTaskData.createTwoTasks()) {
Future<Long> ret = executor
.submit(new CallableCountTask(td.getBeginNumber(), td.getEndNumber(), CountTaskData.DIVISOR));
taskList.add(ret);
}
// Wait until all results are available and combine them at the same time
for (Future<Long> futureTask : taskList) {
try {
resultFuture += futureTask.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
return resultFuture;
}
}
Execute it as Java application and enter different arguments and capture the output here.
C:\MaryZheng\Workspaces\jcg-FutureTask-example\target\classes>java org.jcg.zheng.concurrent.ParallelExecutionDemo executeViaFutureTask Starting parallel execution .... pool-1-thread-2 call. pool-1-thread-1 call. Result (Future): 0 time= 29313 ms C:\MaryZheng\Workspaces\jcg-FutureTask-example\target\classes>java org.jcg.zheng.concurrent.ParallelExecutionDemo submitViaFutureTask Starting parallel execution .... pool-1-thread-2 call. pool-1-thread-1 call. Result (Future): 0 time= 29918 ms C:\MaryZheng\Workspaces\jcg-FutureTask-example\target\classes>java org.jcg.zheng.concurrent.ParallelExecutionDemo submitViaCallableTask Starting parallel execution .... pool-1-thread-1 call. pool-1-thread-2 call. Result (Future): 0 time= 29425 ms
As you seen here, the total execution time is reduced comparing to the sequential execution.
4.3 Cancel Execution Demo
In this step, I will create a CancelExecutionDemo class that cancels the asynchronous job before it completes. Here are the main steps:
- Create a two-thread pool with java.util.concurrent.Executors.
- Create two
FutureTaskobjects, one fromCallableCountTask, the other fromRunnableTask. - Submit or execute the
FutureTask. - Cancel the
FutureTask.
Cancelling a FutureTask may ends up with three results:
- The
FutureTaskis cancelled successfully. - The
FutureTaskalready started and then interrupted. - The
FutureTaskalready started and continued to the end.
CancelExecutionDemo.java
package org.jcg.zheng.concurrent;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import org.jcg.zheng.CountTaskData;
public class CancelExecutionDemo {
public static void main(String[] args) {
Instant begin = Instant.now();
ExecutorService executor = Executors.newFixedThreadPool(2);
FutureTask<Long> runnableTask = new FutureTask<>(new RunnableTask(100), Long.valueOf(10));
FutureTask<Long> callableTask = new FutureTask<>(
new CallableCountTask(0, CountTaskData.MAX_NUMBER, CountTaskData.DIVISOR));
switch (args[0]) {
case "cancelSubmitFutureTask":
cancelSubmitFutureTask(executor, runnableTask, callableTask);
break;
case "cancelExecuteFutureTask":
cancelExecuteFutureTask(executor, runnableTask, callableTask);
break;
case "cancelRunningFutureTask":
cancelRunningFutureTask(executor, runnableTask, callableTask);
break;
}
// Shutdown the ExecutorService
executor.shutdown();
Instant end = Instant.now();
try {
executor.awaitTermination(5, TimeUnit.MINUTES);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(
Thread.currentThread().getName() + " Completed in " + Duration.between(begin, end).toMillis() + " ms.");
}
private static void cancelSubmitFutureTask(ExecutorService executor, FutureTask<Long> runnableTask,
FutureTask<Long> callableTask) {
executor.submit(runnableTask);
// Cancel the job
if (!runnableTask.isDone()) {
boolean cancelStatus = runnableTask.cancel(true);
System.out.println(" runnableTask cancel status " + cancelStatus);
}
executor.submit(callableTask);
// Cancel the job
if (!callableTask.isDone()) {
boolean cancelStatus = callableTask.cancel(true);
System.out.println(" callableTask cancel status " + cancelStatus);
}
}
private static void cancelExecuteFutureTask(ExecutorService executor, FutureTask<Long> runnableTask,
FutureTask<Long> callableTask) {
executor.execute(runnableTask);
// Cancel the job
if (!runnableTask.isDone()) {
boolean cancelStatus = runnableTask.cancel(true);
System.out.println(" runnableTask cancel status " + cancelStatus);
}
executor.submit(callableTask);
// Cancel the job
if (!callableTask.isDone()) {
boolean cancelStatus = callableTask.cancel(true);
System.out.println(" callableTask cancel status " + cancelStatus);
}
}
private static void cancelRunningFutureTask(ExecutorService executor, FutureTask<Long> runnableTask,
FutureTask<Long> callableTask) {
executor.submit(runnableTask);
executor.submit(callableTask);
try {
Thread.sleep(80);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Cancel the job
if (!runnableTask.isDone()) {
boolean cancelStatus = runnableTask.cancel(true);
System.out.println(" runnableTask cancel status " + cancelStatus);
}
// Cancel the job
if (!callableTask.isDone()) {
boolean cancelStatus = callableTask.cancel(true);
System.out.println(" callableTask cancel status " + cancelStatus);
}
}
}
Execute it and capture output here.
Cancel Output
C:\MaryZheng\Workspaces\jcg-FutureTask-example\target\classes>java org.jcg.zheng.concurrent.CancelExecutionDemo cancelSubmitFutureTask runnableTask cancel status true callableTask cancel status true main Completed in 83 ms. C:\MaryZheng\Workspaces\jcg-FutureTask-example\target\classes>java org.jcg.zheng.concurrent.CancelExecutionDemo cancelExecuteFutureTask runnableTask cancel status true callableTask cancel status true main Completed in 78 ms. C:\MaryZheng\Workspaces\jcg-FutureTask-example\target\classes>java org.jcg.zheng.concurrent.CancelExecutionDemo cancelRunningFutureTask pool-1-thread-1 run starts. pool-1-thread-2 call starts. pool-1-thread-1 interrupted. runnableTask cancel status true callableTask cancel status true pool-1-thread-2 call ends. main Completed in 137 ms. C:\MaryZheng\Workspaces\jcg-FutureTask-example\target\classes>
- line 14:
FutureTaskwithRunnableis interrupted. - line 17:
FutureTaskwithCallableis completed.
5. Summary
In this example, I demonstrated how to create a FutureTask object from both Callable and Runnable. I also showed how to execute tasks concurrently to improve performance. Finally, I demonstrated how to cancel a submitted FutureTask and its three possible outcomes.
6. References
7. Download the Source Code
You can download the full source code of this example here: Java FutureTask Example
