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 twoCountTaskData
objects. 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.InterruptedException
and 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
FutureTask
objects withCallableCountTask
. - Submit or execute the
FutureTask
. - Get the
FutureTask
result. - Combine two
FutureTask
‘s results.
In this step, I will demonstrate with three different methods:
executeViaFutureTask
() – creates twoFutureTask
s withCallableCountTask
. InvokingExecutorService
‘sexecute
() method.submitViaFutureTask
() — creates twoFutureTasks
withCallableCountTask
. Invoking ExecutorService’ssubmit()
method.submitViaCallableTask
() – Using ExecutorService’s’s submit() method and usesFuture
to get the result. This is to show the difference betweenFuture
andFutureTask
.
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
FutureTask
objects, one fromCallableCountTask
, the other fromRunnableTask
. - Submit or execute the
FutureTask
. - Cancel the
FutureTask
.
Cancelling a FutureTask may ends up with three results:
- The
FutureTask
is cancelled successfully. - The
FutureTask
already started and then interrupted. - The
FutureTask
already 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:
FutureTask
withRunnable
is interrupted. - line 17:
FutureTask
withCallable
is 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