1.问题引入
创建线程有2中方式:直接继承Thread和实现Runnable接口。但是这2种方式都有一个缺陷,那就是执行完任务之后无法获取执行结果。
如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,但这样使用起来非常麻烦。
所以,自从jdk1.5之后,就提供了Callable和Future,通过他们可以获得任务执行完毕之后得到任务的执行结果。
2.Callable和Runnable
先说一下Runnable接口,在其中只声明了一个run()方法
public interface Runnable
{
public abstract void run();
} 由于run()方法返回值为void类型,所以在执行完任务之后,无法返回任何结果。
Callable位于java.util.concurrent包下,也是一个接口,在里面也是只声明了一个方法:
public interface Callable<V>
{
V call() throws Exception;
} 可以看到,这是一个泛型接口,call()返回的类型就是传递进来的泛型
那么如何使用Callable呢?一般情况下需要配合ExecutorService,在ExecutorService接口中声明了若干个submit方法的重载版本
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task); 第一个submit方法里的参数类型就是Callable。一般我们使用第一和第三个方法3.Future
Future就是对具体的Runnable或者Callable任务的结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果
Future位于java.util.concurrent包下:
public interface Future<V>
{
boolean cancel(boolean mayInterruptIfRunning); //用来取消任务,参数表示是否能够取消正在执行却没有执行完成的任务
boolean isCancelled(); //用来表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回true
boolean isDone(); //表示任务是否已经完成,如果任务完成则返回true
V get() throws InterruptedException, ExecutionException; //用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回
V get(long timeout, TimeUnit unit) //获取执行结果,如果在指定时间内还没有取到结果,就直接返回null
throws InterruptedException, ExecutionException, TimeoutException;
} 简单地说,Future提供了3种功能:
(1)中断任务 (2)判断任务是否完成 (3)获取任务执行结果
因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此有了下面的FutureTask
4.FutureTask
先看一下FutureTask的声明
public class FutureTask<V> implements RunnableFuture<V>
FutureTask实现了RunnableFuture接口,我们看一下RunnableFuture接口的实现
public interface RunnableFuture<V> extends Runnable, Future<V>
{
void run();
} 可以看出FutureTask实现了RunnableFuture接口,RunnableFuture接口继承了Runnable和Future接口,所以FutureTask既可以作为线程执行,又可以作为Future得到Callable的返回值
FutureTask的构造器
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
} 可以看出不论是使用Callable还是Runnable,最终都是转换为Callable5.使用实例
(1)使用Callable+Future获取执行结果
class Task implements Callable<Integer>
{
@Override
public Integer call() throws Exception
{
System.out.println("sub thread is running");
Thread.sleep(3000);
int sum = 0;
for(int i=1; i<100; i++)
sum += i;
return sum;
}
}
public class TestFuture
{
public static void main(String[] args)
{
ExecutorService executorService = Executors.newCachedThreadPool();
Task task = new Task();
Future<Integer> result = executorService.submit(task);
executorService.shutdown();
try
{
Thread.sleep(1000);
}
catch (InterruptedException e1)
{
e1.printStackTrace();
}
try
{
System.out.println(result.get());
}
catch (InterruptedException e)
{
e.printStackTrace();
}
catch (ExecutionException e)
{
e.printStackTrace();
}
} 结果为:
sub thread is running
4950(2)使用Callable+FutureTask获取执行结果
public static void main(String[] args)
{
ExecutorService executorService = Executors.newCachedThreadPool();
Task task = new Task();
FutureTask<Integer> futureTask =new FutureTask<>(task);
executorService.submit(futureTask);
executorService.shutdown();
try
{
Thread.sleep(1000);
}
catch (InterruptedException e1)
{
e1.printStackTrace();
}
try
{
System.out.println(futureTask.get());
}
catch (InterruptedException e)
{
e.printStackTrace();
}
catch (ExecutionException e)
{
e.printStackTrace();
}
}
结果与上面完全相同
本文介绍了Java并发编程中Callable和Future的使用方法,对比了Runnable接口,并详细解释了如何利用Callable结合Future来获取线程执行结果。
294

被折叠的 条评论
为什么被折叠?



