这一块内容和线程池也是息息相关的
线程池的顶级接口是Executor接口,里面只有一个未实现方法是
void execute(Runnable command);
下来是ExecutorService接口,继承自Executor接口,里面多 了很多方法,比较重要的几个方法是
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
提交多个任务,并返回与每个任务对应的Futue。也就是说,任务彼此之间不会相互影响,可以通过future跟踪每一个任务的执行情况,比如是否被取消,是正常完成,还是异常完成。调用该方法的线程会阻塞,直到tasks全部执行完成(正常完成/异常退出)
如果线程在等待invokeAll执行的过程中被中断,那么线程池就会终止所有正在被执行的任务,并抛出异常。
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
给定的超时期满,还没有完成的任务会被取消,即Future.isCancelled()返回true;在超时期之前,无论是正常完成还是异常终止的任务,Future.isCancelled()返回false。
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
提交多个任务,一旦有一个任务完成,就会终止其他任务的执行,如果没有一个任务完成,那么就会抛出异常。
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
如果在超时之前,所有任务已经都是异常终止,那就没有必要在等下去了;如果超时之后,仍然有正在运行或等待运行的任务,那么会抛出TimeoutException。
void shutdown();
终止当前线程池,但是执行中的任务会执行到结束,等待的任务不被执行
List<Runnable> shutdownNow();
终止当前线程池,执行中的任务立即结束,等待的任务不被执行
boolean isShutdown();
如果调用了上面的两个shutdown方法,就返回true
boolean isTerminated();
如果在shutdown之后,所以任务也都结束了,线程池处于终结状态,那么返回true
<T> Future<T> submit(Runnable task, T result);
<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
上面的三个方法都是在线程池中提交一个任务,包括callable类型或者是runable类型。返回future对象
AbstractExecutorService抽象类实现了ExecutorService,对上面的submit方法也有了实现,其最终还是在调用顶级接口Executor中的execute方法,但是AbstractExecutorService并没有实现execute方法,该方法在他的实现类ThreadPoolExecutor中实现。
从上面可以看到submit的参数类型有两种分别是runable和callable,但是在AbstractExecutorService中对submit实现上,都是将两种对象转化成了FutureTask对象,然后将这个转化之后的对象传入execute方法中。
为啥runnable对象和callable对象可以转化成futureTask对象呢?下面从Future接口说起。这个接口的方法如下:
1.cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。
2.isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。
3.isDone方法表示任务是否已经完成,若任务完成,则返回true;
4.get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
5.get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
之后RunnableFuture接口继承了Runnable接口和Future接口,重点,继承了两个接口,然后FutureTask类是RunnableFuture的实现类。
FutureTask的构造方法有两个,一个接受Callable对象,另一个接受runnable对象,但是接受了runnable对象之后会调用Executors.callable()方法,将这个runnable对象转化成callable对象,具体的转化就是通过一个RunnableAdapter类生成一个callabled对象,然后这个callable对象的call方法就是runnable的run方法。那就不用想也明白了,FutureTask中的实现的run方法一定是执行的直接传进来的callable对象或者转化来的call able对象的call方法。事实也是这样。
那这个callable到底是什么,怎么这么牛逼啊,runnable传进来还要转化成他。
public interface Callable<V> {
V call() throws Exception;
}
因为call方法执行完可以返回参数,就这么简单,而run的返回是void。ok,正点来了,当我们起一个线程去做一个任务的时候调用run方法是没有返回值的,如果我们需要的话就只能用callable了。因为call方法是可以返回结果的。
回头再看submit方法,传入一个runnable或者callable,开启一个新的线程去执行,然后返回一个future对象,用来对任务进行操作,是不是很牛逼的设计。
这里上一个例子看看,这个例子就是用线程池加上future来实现多线程的一个方式。
public class FutureDemo {
//创建一个容量为1的线程池
static ExecutorService executorService = Executors.newFixedThreadPool(1);
public static void main(String[] args) throws Exception {
//创建线程并提交线程,同时获取一个future对象
Thread subThread = new Thread(new SubThread());
Future future = executorService.submit(subThread);
//主线程处理其他工作,让子线程异步去执行
mainWork();
//阻塞,等待子线程结束
future.get();
System.out.println("Now all thread done!");
//关闭线程池
executorService.shutdown();
}
//主线程工作
private static void mainWork(){
System.out.println("Main thread start work!");
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Main Thread work done!");
}
/**
* 子线程类
* @author fuhg
*/
private static class SubThread implements Runnable{
public void run() {
// TODO Auto-generated method stub
System.out.println("Sub thread is starting!");
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Sub thread is stopping!");
}
}
}
当然如果要用future实现多线程并不是一定要用线程池,只是ExecutorService中的submit可以直接返回future对象,如果我们自己定义future对象的话,就可以不用线程池来实现。
Callable<String> onlineShopping = new Callable<String>() {
@Override
public Stringcall() throws Exception {
System.out.println("第一步:下单");
System.out.println("第一步:等待送货");
Thread.sleep(5000); // 模拟送货时间
System.out.println("第一步:快递送到");
return "ok";
}
};
FutureTask<String> task = new FutureTask<String>(onlineShopping);
new Thread(task).start();
这里我们自己定义了futuretask对象,然后直接作为参数传递给thread并运行也是ok的,对任务的操作直接用我们自己定义的task就行了。
Callable<String> callable=new Callable<String>() {
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
System.out.println("start call");
Thread.sleep(5000);
System.out.println("end call");
return "hello word";
}
};
ExecutorService executorService = Executors.newFixedThreadPool(1);
//创建线程并提交线程,同时获取一个future对象
Future future = executorService.submit(callable);
//主线程处理其他工作,让子线程异步去执行
//mainWork();
//阻塞,等待子线程结束
try {
System.out.println(future.get());//获取子线程执行结束之后返回的结果
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Now all thread done!");//如果调用了get但是子线程还没有执行结束,那么主线程就会阻塞,那么这句话要在子线程
//执行结束之后,主线程开始执行才能输出。
futuretask有一个重要的属性就是state
private volatile int state; // 注意volatile关键字
/**
* 在构建FutureTask时设置,同时也表示内部成员callable已成功赋值,
* 一直到worker thread完成FutureTask中的run();
*/
private static final int NEW = 0;
/**
* woker thread在处理task时设定的中间状态,处于该状态时,
* 说明worker thread正准备设置result.
*/
private static final int COMPLETING = 1;
/**
* 当设置result结果完成后,FutureTask处于该状态,代表过程结果,
* 该状态为最终状态final state,(正确完成的最终状态)
*/
private static final int NORMAL = 2;
/**
* 同上,只不过task执行过程出现异常,此时结果设值为exception,
* 也是final state
*/
private static final int EXCEPTIONAL = 3;
/**
* final state, 表明task被cancel(task还没有执行就被cancel的状态).
*/
private static final int CANCELLED = 4;
/**
* 中间状态,task运行过程中被interrupt时,设置的中间状态
*/
private static final int INTERRUPTING = 5;
/**
* final state, 中断完毕的最终状态,几种情况,下面具体分析
*/
private static final int INTERRUPTED = 6;
然后还给出了四种可能的结果
Possible state transitions:
* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED
futuretask的cancel方法,get方法都会应用到这些状态。其中get方法会对调用线程进行阻塞。
网友评论