目录
- Java中为什么需要Callable
- Callable和Runnable的区别
- Future和RunnableFuture
- 不使用Callable和Future,仅使用Runnable实现相同功能
Java中为什么需要Callable
在java中有两种创建线程的方法:
一种是继承Thread类,重写run方法:
public class TestMain { | |
public static void main(String[] args) { | |
MyThread t = new MyThread(); | |
t.start(); | |
} | |
} | |
class MyThread extends Thread { | |
public void run() { | |
System.out.println("MyThread running..."); | |
} | |
} |
第二种是使用Runnable创建一个线程:
public class TestMain { | |
public static void main(String[] args) { | |
Runnable r = new Runnable() { | |
public void run() { | |
System.out.println("Thread created with runnable running..."); | |
} | |
}; | |
Thread t = new Thread(r1); | |
t.start(); | |
} | |
} |
其实这两种方式,底层都是执行Thread类的run方法:
无论使用这里的哪种方式创建线程,都无法在线程结束时return一个返回值。但是在非常多的场景下,我们都需要在线程执行结束时,将执行的结果封装为一个返回值返回给主线程(或者调用者线程)。因此java在1.5版本时,在java.util.concurrent包引入了Callable接口,用于线程执行完时return一个返回值。
Callable和Runnable的区别
Runnable和Callable都是接口,分别定义如下:
package java.lang; | |
| |
/** | |
* The <code>Runnable</code> interface should be implemented by any | |
* class whose instances are intended to be executed by a thread. The | |
* class must define a method of no arguments called <code>run</code>. | |
* <p> | |
* @since JDK.0 | |
*/ | |
public interface Runnable { | |
public abstract void run(); | |
} | |
package java.util.concurrent; | |
| |
/** | |
* A task that returns a result and may throw an exception. | |
* Implementors define a single method with no arguments called | |
* {@code call}. | |
* | |
* <p>The {@code Callable} interface is similar to {@link | |
* java.lang.Runnable}, in that both are designed for classes whose | |
* instances are potentially executed by another thread. A | |
* {@code Runnable}, however, does not return a result and cannot | |
* throw a checked exception. | |
* | |
* <p>The {@link Executors} class contains utility methods to | |
* convert from other common forms to {@code Callable} classes. | |
* | |
* @see Executor | |
* @since.5 | |
* @author Doug Lea | |
* @param <V> the result type of method {@code call} | |
*/ | |
public interface Callable<V> { | |
/** | |
* @return computed result | |
* @throws Exception if unable to compute a result | |
*/ | |
V call() throws Exception; | |
} |
可以看出,Callable和Runnable主要有两点区别:
- 有返回值;
- 可以抛出异常(这里抛出的异常,会在future.get()时可以通过ExectionException捕获);
因此可以看出,Callable更加实用。这里举个Callable使用的例子:
Callable callable = new Callable<Integer>() { | |
public Integer call() throws Exception { | |
int i = new Random().nextInt(); | |
try { | |
Thread.sleep(i *); | |
} catch (InterruptedException e) { | |
e.printStackTrace(); | |
} | |
return i; | |
} | |
}; |
虽然Callable接口的call方法可以返回执行结果,但是有两个问题需要解决:
- 线程的创建只能通过Runnable,通过Callable又如何创建线程?
- 如何获取执行结果?
答案是Future和RunnableFuture。
Future和RunnableFuture
Future是一个接口,看下定义:
package java.util.concurrent; | |
| |
/** | |
* A {@code Future} represents the result of an asynchronous | |
* computation. Methods are provided to check if the computation is | |
* complete, to wait for its completion, and to retrieve the result of | |
* the computation. The result can only be retrieved using method | |
* {@code get} when the computation has completed, blocking if | |
* necessary until it is ready. Cancellation is performed by the | |
* {@code cancel} method. Additional methods are provided to | |
* determine if the task completed normally or was cancelled. Once a | |
* computation has completed, the computation cannot be cancelled. | |
* If you would like to use a {@code Future} for the sake | |
* of cancellability but not provide a usable result, you can | |
* declare types of the form {@code Future<?>} and | |
* return {@code null} as a result of the underlying task. | |
* | |
* @see FutureTask | |
* @see Executor | |
* @since.5 | |
* @author Doug Lea | |
* @param <V> The result type returned by this Future's {@code get} method | |
*/ | |
public interface Future<V> { | |
| |
/** | |
* Attempts to cancel execution of this task. This attempt will | |
* fail if the task has already completed, has already been cancelled, | |
* or could not be cancelled for some other reason. If successful, | |
* and this task has not started when {@code cancel} is called, | |
* this task should never run. If the task has already started, | |
* then the {@code mayInterruptIfRunning} parameter determines | |
* whether the thread executing this task should be interrupted in | |
* an attempt to stop the task. | |
* | |
* <p>After this method returns, subsequent calls to {@link #isDone} will | |
* always return {@code true}. Subsequent calls to {@link #isCancelled} | |
* will always return {@code true} if this method returned {@code true}. | |
* | |
* @param mayInterruptIfRunning {@code true} if the thread executing this | |
* task should be interrupted; otherwise, in-progress tasks are allowed | |
* to complete | |
* @return {@code false} if the task could not be cancelled, | |
* typically because it has already completed normally; | |
* {@code true} otherwise | |
*/ | |
boolean cancel(boolean mayInterruptIfRunning); | |
| |
/** | |
* Returns {@code true} if this task was cancelled before it completed | |
* normally. | |
* | |
* @return {@code true} if this task was cancelled before it completed | |
*/ | |
boolean isCancelled(); | |
| |
/** | |
* Returns {@code true} if this task completed. | |
* | |
* Completion may be due to normal termination, an exception, or | |
* cancellation -- in all of these cases, this method will return | |
* {@code true}. | |
* | |
* @return {@code true} if this task completed | |
*/ | |
boolean isDone(); | |
| |
/** | |
* Waits if necessary for the computation to complete, and then | |
* retrieves its result. | |
* | |
* @return the computed result | |
* @throws CancellationException if the computation was cancelled | |
* @throws ExecutionException if the computation threw an | |
* exception | |
* @throws InterruptedException if the current thread was interrupted | |
* while waiting | |
*/ | |
V get() throws InterruptedException, ExecutionException; | |
| |
/** | |
* Waits if necessary for at most the given time for the computation | |
* to complete, and then retrieves its result, if available. | |
* | |
* @param timeout the maximum time to wait | |
* @param unit the time unit of the timeout argument | |
* @return the computed result | |
* @throws CancellationException if the computation was cancelled | |
* @throws ExecutionException if the computation threw an | |
* exception | |
* @throws InterruptedException if the current thread was interrupted | |
* while waiting | |
* @throws TimeoutException if the wait timed out | |
*/ | |
V get(long timeout, TimeUnit unit) | |
throws InterruptedException, ExecutionException, TimeoutException; | |
} |
可以看出,Future可以用来表示线程的未来执行结果:一个容器,这个容器内将来存放的是线程的执行结果,线程执行完之前该容器内没有值,但是线程一旦执行成功(Callable的call方法返回之后),就会将结果存入该容器。从Future的接口定义可看出,Future不仅支持阻塞获取执行结果,还支持取消任务的执行,判断任务是否执行完成等。因此通过Future,主线程(或者调用者线程)可以跟进子现场的执行情况。
Callable其实和Runnable很像,都会执行一个任务,只不过Callable可以返回执行的结果。一般将执行结果封装到Future,调用者线程即可以通过Future获取Callable的执行结果了。因此,一般Callable会和Future搭配使用。
但是问题来了:java创建线程,需要Runnable,获取执行结果又需要Future。因此RunnableFuture来了:
可以看出,通过RunnableFuture,既可以创建线程,又可以获取线程的执行结果,当然RunnableFuture也是一个接口,我们一般情况下会使用它的具体实现类FutureTask。
那可能又有人要问了,Callable又是如何建立联系的呢?看下FutureTask的使用方式就明白了:
public class TestMain { | |
public static void main(String[] args) { | |
Callable callable = new Callable<Integer>() { | |
public Integer call() throws Exception { | |
int i = new Random().nextInt(); | |
try { | |
Thread.sleep(i *); | |
} catch (InterruptedException e) { | |
e.printStackTrace(); | |
} | |
return i; | |
} | |
}; | |
| |
/** | |
* callable创建futureTask | |
* FutureTask实现了RunnableFuture接口,因此即是Runnable又是Future | |
* 作为Runnable可以传入Thread创建线程并执行 | |
* 作为Future,可以用来获取执行的结果。 | |
* 这里创建出来的futureTask对象有人称为"具柄"或者"存根",大家可以理解为用来获取线程执行结果的一个"引用"即可。 | |
*/ | |
FutureTask<Integer> futureTask = new FutureTask<Integer>(callable); | |
| |
// 作为Runnable使用 | |
Thread thread = new Thread(futureTask); | |
thread.start(); | |
| |
try { | |
// 作为Future使用 | |
Integer integer = futureTask.get(); | |
System.out.println(integer); | |
} catch (InterruptedException e) { | |
e.printStackTrace(); | |
} catch (ExecutionException e) { | |
e.printStackTrace(); | |
} | |
} | |
} |
因此FutureTask是Callable到Runnable的桥梁。
不使用Callable和Future,仅使用Runnable实现相同功能
下面我们看下,如果不使用Callable和Future,仅使用Runnable如何实现返回值。
public class TestMain { | |
public static void main(String[] args) { | |
MyRunnable myRunnable = new MyRunnable(); | |
Thread t = new Thread(myRunnable); | |
t.start(); | |
Object o = myRunnable.get(); | |
System.out.println(o); | |
} | |
} | |
| |
class MyRunnable implements Runnable { | |
// 存储执行结果 | |
private Object outCome = null; | |
| |
public void run() { | |
int i = new Random().nextInt(); | |
try { | |
Thread.sleep(i *); | |
} catch (InterruptedException e) { | |
e.printStackTrace(); | |
} | |
// 存储执行结果 | |
outCome = i; | |
// 产出结果后唤醒等待的get方法 | |
synchronized (this) { | |
notifyAll(); | |
} | |
} | |
| |
public synchronized Object get() { | |
while(outCome == null) { | |
try { | |
// 等待产出结果 | |
wait(); | |
} catch (InterruptedException e) { | |
e.printStackTrace(); | |
} | |
} | |
return outCome; | |
} | |
} |
可以看出,通过Runnable实现更加麻烦,因此这也体现出了Callable+Future的优势。