Callable
通常来说,创建多线程的方式有两种,继承Thread或者实现Runnable接口。但是这两种方式中的run方法返回值都是void,当我们希望在线程执行完的时候返回一个结果时,我们可以使用实现Callable接口来创建线程。Callable接口的源码如下:
public interface Callable<V> { V call() throws Exception; }
接口中的call方法相当于Runnable中的run方法,只不过call会返回一个泛型值。
Future
Future是一个接口,用来获取一个异步处理的结果,源码如下:
public interface Future<V> { boolean cancel(boolean mayInterruptIfRunning); boolean isCancelled(); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
其中,
cancel方法是用来取消任务的执行,mayInterruptIfRunning参数是用来判断是否终止正在运行的任务,如果为true,就代表可以取消正在运行的任务,取消成功了返回true,如果为false,则表示不能终止正在运行的任务,必须等待其运行结束,返回false。如果任务已经结束,返回false。如果任务还没有开始,返回false。
isCancelled方法用来判断任务在完成前是否被取消,如果被取消了,则返回true,如果没有被取消,返回false。
isDone方法用来判断任务是否执行结束,无论是执行完成或者中途取消或者发生异常,都返回true,没有结束则返回false。
get方法用来阻塞式的获取任务执行的结果,没有执行结束就一直阻塞,一直到执行结束返回结果。
get方法带了timeout参数用来指定时间限制,如果超过了时间还没有返回结果则抛出异常。
FutureTask
FutureTask是一个类,因为Future只是一个接口,并不能拿来创建对象。FutureTask就是Future的实体类。
不过FutureTask是实现的RunnableFuture接口,而RunnableFuture接口是实现了Runnable接口和Future接口。
public class FutureTask<V> implements RunnableFuture<V> {
public interface RunnableFuture<V> extends Runnable, Future<V> { void run(); }
可以看到FutureTask中也有run方法,所以可以使用线程池的execute方法让其执行,也可以使用Thread的start方法让其执行。
FutureTask有一个state属性,state的值有如下几种:
private volatile int state; private static final int NEW = 0; private static final int COMPLETING = 1; private static final int NORMAL = 2; private static final int EXCEPTIONAL = 3; private static final int CANCELLED = 4; private static final int INTERRUPTING = 5; private static final int INTERRUPTED = 6;
NEW表示任务刚刚被创建或者还没有被执行完的任务,是一个初始状态。在FutureTask被构造方法实例化后,会将state属性置为NEW。
COMPLETING表示任务已经执行完成或者执行过程中发生异常,但是执行结果或者异常报告还没有被保存到outcome字段中,outcome字段用来保存执行的结果或者异常的报告。COMPLETING是一个中间态,持续时间很短,最终将跳到NORMAL或EXCEPTIONAL或CANCELLED或INTERRUPTING或INTERRUPTED状态。
NORMAL表示任务已经执行完成,并且执行结果已经保存到outcome字段中,是一个最终态。
EXCEPTIONAL表示任务在执行过程发生异常,并且异常报告已经保存到outcome字段中,是一个最终态。
CANCELLED表示任务还没有执行时调用了cancel(false)方法使任务取消了,是一个最终态。
INTERRUPTING表示任务在执行还没有执行或者在执行过程中调用了cancel(false)方法要取消任务但是还没有取消完成,当取消完成了就跳到INTERRUPTED,是一个中间态。
INTERRUPTED表示任务在执行还没有执行或者在执行过程中调用了cancel(false)方法取消任务完成,是一个最终态。
其中,所有值大于COMPLETING的状态都表示任务已经执行完成(任务正常执行完成,任务执行异常或者任务被取消)。
代码应用:
public class FuturetaskTest { public static void main(String[] args) { FutureTask task = new FutureTask(new Callable() { @Override public Integer call() throws Exception { Thread.sleep(1000);//模拟任务处理时间 System.out.println("任务已经启动!!!"); Integer result = 0; for (int i = 0; i < 5000000; i++) { result += i; } return result; } }); System.out.println("主线程在执行!"); //第一种方式,使用线程池调用 ExecutorService executor = Executors.newFixedThreadPool(10); executor.execute(task); try { System.out.println("任务是否完成:"+task.isDone()); Thread.sleep(2000);//让任务先执行 System.out.println("取消任务:"+task.cancel(false)); System.out.println("任务是否被取消:"+task.isCancelled()); System.out.println("任务执行结果:"+task.get()); } catch (Exception e) { e.printStackTrace(); } System.out.println("主线程执行完毕!"); System.out.println("任务是否完成:"+task.isDone()); executor.shutdown(); //第二种方式,使用Thread调用 Thread thread = new Thread(task); thread.start(); try { System.out.println("任务执行结果:"+task.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
执行结果:
可以看到,在任务已经执行的时候调用cancel(false)方法并没有取消任务。而且在第二次尝试启动任务时,任务并没有启动,而是直接返回了第一次执行的结果。所以FutureTask的任务只会执行一次,在返回结果是会判断当前任务的state是否等于NEW,如果不为NEW则说明任务或者已经执行过,或者已经被取消,直接返回。
FutureTask使用场景
FutureTask可以用来异步获取处理结果,创建一个FutureTask来处理运算,在主线程的合适位置调用get获取处理结果,去除主线程的等待时间,将等待时间可以去处理其他复杂的业务逻辑。
还有在很多高并发的环境下,往往我们只需要某些任务只执行一次。这种使用情景FutureTask的特性恰能胜任。举一个例子,假设有一个带key的连接池,当key存在时,即直接返回key对应的对象;当key不存在时,则创建连接。