Callable
and Future
are introduced in subsequent versions of Java, Callable
similar to Runnable
the interface, both classes that implement Callback
the interface and Runnable
classes that implement the interface can be used for tasks executed by threads.
The following are the relevant source codes of the two interfaces:
// /xref/libcore/ojluni/src/main/java/java/util/concurrent/Callable.java
public interface Callable<V> {
V call() throws Exception;
}
// /xref/libcore/ojluni/src/main/java/java/lang/Runnable.java
public interface Runnable {
public abstract void run();
}
It can be seen from the above source code: Callable.call()
the method has a return value and can throw an exception; Runable.run()
the method has no return value and will not throw an exception.
Callable
The interface can be seen as Runnable
a supplement to the interface, and Runnable
the difference with is that it returns the result of executing the task and can throw an exception, which is generally ThreadPoolExecutor
used in conjunction.
Future
is an interface that can cancel Runnable
or task, determine whether the task is canceled, query whether the task is completed, and obtain the task result. Callable
The following is Future
the relevant source code:
public interface Future<V> {
// 取消任务的执行。任务已经完成或者已经被取消时调用此方法,会返回 false
boolean cancel(boolean mayInterruptIfRunning);
// 判断任务是否被取消
boolean isCancelled();
// 判断任务是否完成。正常完成、异常以及被取消,都将返回 true
boolean isDone();
// 阻塞地获取任务的执行结果
V get() throws InterruptedException, ExecutionException;
// 在一定时间内,阻塞地获取任务的执行结果
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
But since Future
is just an interface, in order to use the functions in it, you must use its indirect implementation class FutureTask
, which represents the result of asynchronous calculation.
FutureTask
is RunnableFuture
the implementation class of the interface, and RunnableFuture
the interface implements Runnable
the and Future
interfaces. Therefore, FutureTask
it can be handed over Thread
or Executor
executed.
Here is FutureTask
the inheritance relationship for :
According to FutureTask.run()
the timing when the method is executed, FutureTask
it can be in the following three states:
- Unstarted:
FutureTask.run()
before the method is executed, it is in the unstarted state; - Started: The method is in the started state
FutureTask.run()
during execution ;FutureTask
- Completed:
FutureTask.run()
After the method execution is completed, it ends normally, or it is executedFutureTask.cacel(boolean)
, canceled, or executedFutureTask.run() 方法时抛出异常而结束
, it is in the completed state;
The following is FutureTask
a state transition diagram for :
For FutureTask.get()/FutureTask.get(long, TimeUnit)
the method:
- When
FutureTask
is in the unstarted or started state, executingFutureTask.get()/FutureTask.get(long, TimeUnit)
the method will cause the thread to block; - When
FutureTask
is in the completed state, executingFutureTask.get()/FutureTask.get(long, TimeUnit)
the method will cause the calling thread to return the result immediately or throw an exception;
For FutureTask.cancel(boolean)
the method:
- When
FutureTask
is not started, executingFutureTask.cancel(boolean)
the method will cause this task to never be executed; - When
FutureTask
is in the started state, the executeFutureTask.cancel(true)
method will try to stop the task by interrupting the thread executing the task; - When
FutureTask
in the started state, the executionFutureTask.cancel(false)
method will not affect the thread of the task being executed (let the task being executed run to completion); - When
FutureTask
the is in a completed state, the executeFutureTask.cancel(boolean)
method will returnfalse
;
You can hand FutureTask
over to Executor
for execution, or you can <T> Future<T> ExecutorService.submit(Runnable, T)/Future<?> ExecutorService.submit(Runnable)
return an Future
object through the method, but since Future
is an interface, FutureTask
the object is generally returned here. Then you can call FutureTask.get()/get(long, TimeUnit)
the method or FutureTask.cancel(boolean)
method.
It can be used when a thread needs to wait for another thread to complete a task before it can continue FutureTask
. Suppose there are multiple threads performing several tasks. When multiple threads try to execute the same task at the same time, only one thread is allowed to execute the task, and other threads need to wait for the task to complete before continuing to execute.
Here is FutureTask
the code for :
private final ConcurrentHashMap<Object, Future<String>> taskCache = new ConcurrentHashMap<>();
private String executionTask(final String taskName) {
while (true) {
Future<String> future = taskCache.get(taskName); // 1.1 2.1
if (future == null) {
Callable<String> task = new Callable<String>() {
@Override
public String call() throws Exception {
return taskName;
}
};
FutureTask<String> futureTask = new FutureTask<>(task);
future = taskCache.putIfAbsent(taskName, futureTask); // 1.3
if (future == null) {
future = futureTask;
futureTask.run(); // 1.4 执行任务
}
}
try {
return future.get(); // 1.5 2.2 线程在此等待任务执行完成
} catch (ExecutionException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
The following is a schematic diagram of the code:
When two threads try to execute the same task at the same time, if Thread 1 executes 1.3 and Thread 2 executes 2.1, then Thread 2 will wait at 2.2 until Thread 1 executes 1.4 before Thread 2 can return from 2.2.
Here's FutureTask
how to use:
public class Test {
public static void main(String[] args) throws InterruptedException, ExecutionException {
MyCallable myCallable = new MyCallable();
FutureTask<String> futureTask = new FutureTask(myCallable);
Thread thread = new Thread(futureTask);
thread.start();
Thread.sleep(100);
futureTask.cancel(true); // 1
System.out.println("future is cancel: " + futureTask.isCancelled());
if (!futureTask.isCancelled()) {
System.out.println("future is cancelled");
}
System.out.println("future is done: " + futureTask.isDone());
if (!futureTask.isDone()) {
System.out.println("future get = " + futureTask.get()); // 2
} else {
System.out.println("task is done");
}
}
}
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName() + "-call is running");
Thread.sleep(5000);
return "Hello World";
}
}
The following is the execution result:
Try to cancel the execution of the task at Note 1. If the method returns when the task is completed or canceled false
, if it can cancel the task that has not been completed, return true
, because in the above code because the task is still in a dormant state, it can be Cancel the task. At Note 2, FutureTask.get()
the method will block the current thread until the execution of the task is completed and the result is returned.
For use with Callable
and :Future
public class Test {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Callable<String> call = new MyCallable();
Future<String> future = executorService.submit(call);
executorService.shutdown();
Thread.sleep(5000);
System.out.println("主线程休眠 5 秒,当前时间: " + System.currentTimeMillis());
String str = future.get();
System.out.println("Future 已拿到数据,str = " + str + "; 当前时间为: " + System.currentTimeMillis());
}
}
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("进入 call 方法,开始休眠,休眠的时间为: " + System.currentTimeMillis());
Thread.sleep(10000);
return "Hello World";
}
}
Results of the:
In the above code, future
it is directly put into the thread pool for execution. Judging from the execution results, although the main thread sleeps for 5s, but call()
the result of the task is obtained from the method execution, the time difference between them is 10s, indicating thatFutureTask.get() 方法会阻塞当前线程直到任务完成。
ExecutorService.shutdow()
method, the thread pool is in SHUTDOWN
the state. At this time, the thread pool cannot receive new tasks, and it will wait for all tasks to be executed. If ExecutorService.shutdownNow()
the method is called, the thread is in STOP
the state, and the thread pool cannot accept new tasks at this time, and will try to terminate the result being executed.
FutureTask
The same effect can also be achieved by :
public class Test {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Callable<String> call = new MyCallable();
FutureTask<String> task = new FutureTask<>(call);
executorService.submit(task);
executorService.shutdown();
Thread.sleep(5000);
System.out.println("主线程休眠 5 秒,当前时间: " + System.currentTimeMillis());
String str = task.get();
System.out.println("Future 已拿到数据,str = " + str + "; 当前时间为: " + System.currentTimeMillis());
}
}
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("进入 call 方法,开始休眠,休眠的时间为: " + System.currentTimeMillis());
Thread.sleep(10000);
return "Hello World";
}
}
Results of the:
The above combination brings the following changes: If there is a scenario, it takes 10s for method A to return a piece of data, and the code behind method A takes 20s to run, but in the execution result of 20s, only the latter 10s depends on the execution result of method A . If the same method is used as in the past, 10s of time will inevitably be wasted. If the first two combinations are used, the efficiency will increase:
- First put the content of method A into the method
Callable
of the implementation classcall()
; - Execute task A through the thread pool in the main thread;
- Execute the code in the following method for 10s that does not depend on the running result of method A
- Obtain the running result of method A, and execute the code that depends on the running result of method A for 10 seconds in the following method;
In this way, the efficiency of the code is improved, and the program does not need to be stuck at the A method.