在Java中创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口。这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果。
在Java中,如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。
在学习Callable和FutureTask之前,我们先看一下java.lang.Runnable吧,它是一个接口,在它里面只声明了一个run()方法:
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface Runnable
is used
* to create a thread, starting the thread causes the object's
* run
method to be called in that separately executing
* thread.
*
* The general contract of the method run
is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
由于run()方法返回值为void类型,所以在执行完任务之后无法返回任何结果。
Callable位于java.util.concurrent包下,它也是一个接口,在它里面也只声明了一个方法,只不过这个方法叫做call():
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}.
*
* 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.
*
*
The {@link Executors} class contains utility methods to
* convert from other common forms to {@code Callable} classes.
*
* @see Executor
* @since 1.5
* @author Doug Lea
* @param the result type of method {@code call}
*/
@FunctionalInterface
public interface Callable {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
可以看到,这是一个泛型接口,call()函数返回的类型就是传递进来的V类型。
那么怎么使用Callable呢?一般情况下是配合ExecutorService来使用的,在ExecutorService接口中声明了若干个submit方法的重载版本:
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future> submit(Runnable task);
Future类位于java.util.concurrent包下,它是一个接口:
public interface Future {
boolean cancel( boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get( long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
在Future接口中声明了5个方法,下面依次解释每个方法的作用:
Future提供了三种功能:
因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask。
CallableTask.java
public class CallableTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int hours=5;
int amount = 0;
while(hours>0){
System.out.println("我正在工作,剩余时间 "+hours+"小时");
amount++;
hours--;
Thread.sleep(1000);
}
return amount;
}
}
TestDemo.java
public class TestDemo {
public static void main(String args[]) throws ExecutionException {
CallableTask worker = new CallableTask();
FutureTask jiangong = new FutureTask(worker);
new Thread(jiangong).start();
while(!jiangong.isDone()){
try {
System.out.println("获取结果"+jiangong.get());
System.out.println("任务是否取消"+jiangong.isCancelled());
System.out.println("任务是否执行"+jiangong.isDone());
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int amount;
try {
amount = jiangong.get();
System.out.println("工作做完了,上交了"+amount);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
运行的结果:
package com.lidong.demo;
import java.util.concurrent.Callable;
/**
* @项目名称:lidong-dubbo
* @类名:Task
* @类的描述:
* @作者:lidong
* @创建时间:2017/2/21 下午3:42
* @公司:chni
* @QQ:1561281670
* @邮箱:[email protected]
*/
public class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+" 子线程在进行计算开始");
Thread.sleep(3000);
int sum = 0;
for(int i=0;i<100;i++)
sum += i;
System.out.println(Thread.currentThread().getName()+" 子线程在进行计算结束");
return sum;
}
}
Test.java
package com.lidong.demo;
import java.util.concurrent.*;
public class Test {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
Task task = new Task();
FutureTask futureTask = new FutureTask(task);
executor.submit(futureTask);
executor.shutdown();
System.out.println(Thread.currentThread().getName()+" 主线程在执行任务");
try {
System.out.println(Thread.currentThread().getName()+ " task运行结果"+futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ " 所有任务执行完毕");
}
}
运行结果: