1.Callable和Runnable接口
Q1: 二者之间有什么不同?
A1: 共有2点主要不同。其一Callable接口实现的方法可以有返回值,而Runnable的实现方法没有返回值;其二,Callable方法在调用的时候会抛出异常,而Runnable方法在调用的时候则不会!
public interface Callable<T>{
T call() throws Exception;
}
public interface Runnable{
void run();
}
Q2: 既然Callable接口能实现Runnable接口的所有功能,那么Runnable接口的存在意义是什么呢?
A2: Runnable接口是jdk1.0版本的产物,后来发现线程无法返回结果这点有很大局限性,则在jdk1.5版本后增加了Callable接口,这个改动本可以在原Runnable基础上添加,但是为了保持java语言能兼容旧版本 jdk就新添加了借口Callable。
There are two ways of creating threads – one by extending the Thread class and other by creating a thread with a Runnable. However, one feature lacking in Runnable is that we cannot make a thread return result when it terminates, i.e. when run() completes. For supporting this feature, the Callable interface is present in Java.
对于Callable接口:实现call()方法,且该方法可能抛出异常、完成时会返回结果
对于Runnable接口:实现run()方法,能创建线程
public Object call() throws Exception;
即:我们使用Runnable接口创建线程、我们用 Future接口得到结果。
2.FutureTask和Thread实例
call()方法返回的结果将会存放在一个Future对象中,Future对象并不会立即存下这个结果,而是在将来的某个时间得到这个结果(一旦Callable对象返回)。
Java库里提供了一个新的类FutureTask,它实现了Runnable和Future接口,因而具有两者共有的功能。
因而:一个FutureTask对象可以通过Callable对象作为构造函数参数构造出来;FutureTask可以作为Thread构造函数的参数来创建一个Thread对象。所以,间接地看,线程是通过Callable接口创建的。但这里需要再强调一次:不能直接通过Callable接口建立线程。
但是:实质上使用Callable借口实现了很多类似接口的功能。这里只需将Callable对象作为FutureTask的构造函数参数传入即可。由于FutureTask类implements Runnable和 Callable接口,所以可以将其作为Thread构造函数的参数传入。
class TaskImpRunnable implements Runnable{ } class TaskImpCallable implements Callable{ } public class Main{
public static void main(String[] args){
}TaskImpRunnable task1 = new TaskImpRUnnable(); TaskImpCallable task2 = new TaskImpCallable(); Thread t1 = new Thread(task1); //Callable needs FutureTask class as a bridge FutureTask future = new FutureTask(task2); Thread t2 = new Thread(future); t2.start();
}
// Java program to illustrate Callable and FutureTask
// for random number generation
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
class CallableExample implements Callable
{
public Object call() throws Exception
{
Random generator = new Random();
Integer randomNumber = generator.nextInt(5);
Thread.sleep(randomNumber * 1000);
return randomNumber;
}
}
public class CallableFutureTest
{
public static void main(String[] args) throws Exception
{
// FutureTask is a concrete class that
// implements both Runnable and Future
FutureTask[] randomNumberTasks = new FutureTask[5];
for (int i = 0; i < 5; i++)
{
Callable callable = new CallableExample();
// Create the FutureTask with Callable
randomNumberTasks[i] = new FutureTask(callable);
// As it implements Runnable, create Thread
// with FutureTask
Thread t = new Thread(randomNumberTasks[i]);
t.start();
}
for (int i = 0; i < 5; i++)
{
// As it implements Future, we can call get()
System.out.println(randomNumberTasks[i].get());
// This method blocks till the result is obtained
// The get method can throw checked exceptions
// like when it is interrupted. This is the reason
// for adding the throws clause to main
}
}
}
// Java program to illustrate Runnable
// for random number generation
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
class RunnableExample implements Runnable
{
// Shared object to store result
private Object result = null;
public void run()
{
Random generator = new Random();
Integer randomNumber = generator.nextInt(5);
// As run cannot throw any Exception
try
{
Thread.sleep(randomNumber * 1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
// Store the return value in result when done
result = randomNumber;
// Wake up threads blocked on the get() method
synchronized(this)
{
notifyAll();
}
}
public synchronized Object get()
throws InterruptedException
{
while (result == null)
wait();
return result;
}
}
// Code is almost same as the previous example with a
// few changes made to use Runnable instead of Callable
public class RunnableTest
{
public static void main(String[] args) throws Exception
{
RunnableExample[] randomNumberTasks = new RunnableExample[5];
for (int i = 0; i < 5; i++)
{
randomNumberTasks[i] = new RunnableExample();
Thread t = new Thread(randomNumberTasks[i]);
t.start();
}
for (int i = 0; i < 5; i++)
System.out.println(randomNumberTasks[i].get());
}
}
3.Future Demo
package com.fqy.mix;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
import junit.framework.Test;
public class FutureDemoIn {
private static final ExecutorService threadpool = Executors.newFixedThreadPool(3);
public static void main(String args[]) throws InterruptedException, ExecutionException {
FactorialCalculator task = new FactorialCalculator(10);
System.out.println("Submitting Task ...");
Future<Long> future = threadpool.submit(task);
System.out.println("Task is submitted");
while (!future.isDone()) {
System.out.println("Task is not completed yet....");
Thread.sleep(1); // sleep for 1 millisecond before checking again }
System.out.println("Task is completed, let's check result");
long factorial = future.get();
System.out.println("Factorial of 10 is : " + factorial);
threadpool.shutdown();
}
}
private static class FactorialCalculator implements Callable<Long> {
private final int number;
public FactorialCalculator(int number) {
this.number = number;
}
@Override
public Long call() {
long output = 0;
try {
output = factorial(number);
} catch (InterruptedException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
return output;
}
private long factorial(int number) throws InterruptedException {
if (number < 0) {
throw new IllegalArgumentException("Number must be greater than zero");
}
long result = 1;
while (number > 0) {
Thread.sleep(100); // adding delay for example
result = result * number;
number--;
}
return result;
}
}
}
运行结果:
Submitting Task ...
Task is submitted
Task is not completed yet....
Task is completed, let's check result
Factorial of 10 is : 3628800
简单demo:
package com.fqy.mix;
import java.io.IOException;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class RetValThread {
public static void main(String[] args) {
RetValThreadUtil.demonstrate();
}
}
class RetValThreadUtil {
public static void demonstrate() {
ExecutorService executor = Executors.newCachedThreadPool();
Future<Integer> future = executor.submit(new CallableTask());
executor.shutdown();
try {
System.out.println("Sleeping time is: " + future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
IOException ex = (IOException) (e.getCause());
System.out.println(ex.getMessage());
}
}
}
class CallableTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
Random random = new Random();
int retVal = random.nextInt(4000);
if (retVal > 2000)
throw new IOException("Sleeping for too long!");
System.out.println("Sleeping started.");
Thread.sleep(retVal);
System.out.println("Sleeping terminated.");
return retVal;
}
}
运行结果:
Sleeping started.
Sleeping terminated.
Sleeping time is: 699
4.己见
Callable接口是为了解决传统Runnable接口无法返回运行结果问题的;
Future是存储Callable接口的callable()方法返回值对象而存在的;
FutureTask实现了Callable和Runnable接口。
这里可以找到相关代码和笔记。