Core Java

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.

Java FutureTask class diagram
Figure 1 FutureTask Class Diagram

In this example, I will demonstrate the following:

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 two CountTaskData 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() – invokes countService.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, catches java.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:

  1. Create a two-thread pool with java.util.concurrent.Executors.
  2. Create two FutureTask objects with CallableCountTask.
  3. Submit or execute the FutureTask.
  4. Get the FutureTask result.
  5. Combine two FutureTask‘s results.

In this step, I will demonstrate with three different methods:

  • executeViaFutureTask() – creates two FutureTasks with CallableCountTask. Invoking ExecutorService‘s execute() method.
  • submitViaFutureTask() — creates two FutureTasks with CallableCountTask. Invoking ExecutorService’s submit() method.
  • submitViaCallableTask() – Using ExecutorService’s’s submit() method and uses Future to get the result. This is to show the difference between Future and FutureTask.

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:

  1. Create a two-thread pool with java.util.concurrent.Executors.
  2. Create two FutureTask objects, one from CallableCountTask, the other from RunnableTask.
  3. Submit or execute the FutureTask.
  4. 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 with Runnable is interrupted.
  • line 17: FutureTask with Callable 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

Download
You can download the full source code of this example here: Java FutureTask Example

Mary Zheng

Mary has graduated from Mechanical Engineering department at ShangHai JiaoTong University. She also holds a Master degree in Computer Science from Webster University. During her studies she has been involved with a large number of projects ranging from programming and software engineering. She works as a senior Software Engineer in the telecommunications sector where she acts as a leader and works with others to design, implement, and monitor the software solution.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button