再说Runnable、Callable、Future、线程池
2017-09-27 16:26
253 查看
Runnable接口
Runnable接口是关于线程的开发中使用最多的接口,在Handler中,我们可以post一个Runnable任务;我们经常使用的Thread也是Runnable接口的实现类。
Runnable接口的run方法无返回值,也无异常抛出,也就是说我们在run方法中的任务一旦执行,我们无法获知任务是否执行完毕以及执行的结果。
源码
public interface Runnable { public abstract void run(); }
Callable接口
Callable接口与Runable接口很类似,它也提供了一个类似run方法的
call方法用于执行任务,但与
run方法不同的是,
call方法有返回值,且会抛出异常。
源码
public interface Callable<V> { V call() throws Exception; }
Future接口
Future接口与Runable接口和Callable接口都不一样,它没有类似的run方法或
call方法,所以Future接口不能用来执行任务,并且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; }
Runable转为Callable
可以通过Executors类的callable(Runnable task, T result)静态方法,把一个Runable任务转换为Callable任务。
示例:
Callable<Object> callable = Executors.callable(new MyRunnable());
线程池(ExecutorService)
Executor源码:public interface Executor { void execute(Runnable command); }
ExecutorService源码:
public interface ExecutorService extends Executor { void shutdown(); List<Runnable> shutdownNow(); boolean isShutdown(); boolean isTerminated(); boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException; <T> Future<T> submit(Callable<T> task); <T> Future<T> submit(Runnable task, T result); Future<?> submit(Runnable task); <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException; <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException; <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; }
线程池被设计用来更好的控制与利用线程资源,节省线程的性能开销,提升线程执行效率。
由上述源码可知,Executor接口只有一个
void execute(Runnable command);方法,只能用于执行Runable任务,不能执行Callable任务,所以为了能执行Callable任务,Java提供了ExecutorService接口,它继承于Executor,并扩展了一些方法,用来执行Callable任务,并且还提供了关闭线程等其他方法,所以在实际开发中我们一般都使用ExecutorService接口来做线程池的相关操作;
我们现在小小的总结一下,Java中的线程池可以执行哪几种任务:
- Runable任务
- Callable任务
- Collection任务,其实本质上还是Callable任务
一定要注意:Java线程池并不能执行Future任务,原因之前也说了,因为Future接口根本没有用于执行任务的方法,并且本来在设计上也不是用来执行任务的,而是用于获取任务执行的结果的。
submit方法与execute方法的区别
execute方法是Executor接口定义的,它只能用于执行Runable任务,并且无返回值。submit方法是ExecutorService中定义的,它既能用于执行Runable任务,也可以用于执行Callable任务,并且必有返回值,且返回值为
Future<T>,利用返回值
Future<T>,我们可以获取任务执行的结果,以及中断任务的执行等操作。
因为execute方法无返回值,submit方法有返回值,所以如果一个任务不需要返回值,可以使用execute方法执行一个Runable任务,这样比较简单,而如果一个任务需要返回值,或者需要中断任务执行等操作,就需要使用submit方法,以实现更加精细的控制。 当然,不管需不需要返回值,都可以使用submit方法,看个人选择罢了。
线程池的使用
为方便使用,Java提供了Executors类,以静态工厂的方式提供各种类型的线程池,常见的有以下四种:public static ExecutorService newCachedThreadPool(); public static ExecutorService newFixedThreadPool(int nThreads); public static ExecutorService newSingleThreadExecutor(); public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize);
上面每种线程池最终都实现了ExecutorService接口,所以它们都既可以执行Runable任务也可以执行Callable任务。如果Java提供的线程池不能满足需求,还可以通过
ThreadPoolExecutor类自定义线程池。
下面以
newSingleThreadExecutor为例演示线程池执行Runable任务和Callable任务的用法:
执行Runable任务
因为Runable任务无返回值,所以使用上相对比较简单。使用execute方法
... private ExecutorService mExecutor = Executors.newSingleThreadExecutor(); ... mExecutor.execute(new Runnable() { @Override public void run() { //to do something } });
使用submit方法
... private ExecutorService mExecutor = Executors.newSingleThreadExecutor(); ... mExecutor.submit(new Runnable() { @Override public void run() { //to do something } });
执行Callable任务
对于Callable任务,只能使用submit方法... private ExecutorService mExecutor = Executors.newSingleThreadExecutor(); ... Future<String> future = mExecutor.submit(new Callable<String>() { @Override public String call() throws Exception { String result = null; //to do something return result; } });
通过上述代码我们利用线程池执行了一个Callable任务,并返回一个
Future<String>对象,利用
Future<String>对象的
get方法我们可以获取执行的结果:
Future<String> future = mExecutor.submit(new Callable<String>() { @Override public String call() throws Exception { String result = null; //to do something return result; } }); try { String result = future.get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); }
可以看到,我们在下方调用
future.get()方法尝试获取任务的执行结果,这里我们要注意,如果此时任务还在执行中,
future.get()方法会阻塞线程,一直阻塞到任务执行完毕返回时才释放,这种方式在Android开发中肯定是不行的,因为在Android的主线程中不能做耗时操作,否则就会发生ANR,所以我们在获取结果时,不能直接使用
future.get()方法获取结果,除非我们知道这个任务已经执行完了,但是我们如何才能知道任务已经执行完毕了呢?虽然Future类中提供了
isDone方法用来判断任务是否执行完毕,但是
isDone是瞬时的方法,没法一直监听,难道我们还得写一个死循环一直查询任务是否执行完毕吗?No,No,No,我们有
FutureTask类。
FutureTask类
FutureTask类实现了RunnableFuture接口,而RunnableFuture接口又继承了Runnable接口和Future接口,所以FutureTask类本质上是Runnable接口的实现类,且兼具Future接口的特性,我们知道线程池的execute方法和
submit方法都可以执行Runable任务,所以同样可以执行FutureTask任务。
FutureTask类的构造方法如下:
public FutureTask(Callable<V> callable); public FutureTask(Runnable runnable, V result);
看到了FutureTask类的构造方法,我们又知道了FutureTask既可以装载Runnable任务,又可以装载Callable任务。
最最最最关键的是FutureTask类有一个
done方法,该方法在任务执行完毕时自动回调,我们可以重写该方法并在该方法中调用
get()方法获取任务执行的结果,并且因为任务已经执行完毕,此时调用
get()方法可以直接得到结果,所以并不会阻塞线程,简直太完美了。
上面也说了,FutureTask类本质上是Runnable的实现类,所以它本身并不能执行任务,一样要依靠
execute方法或
submit方法去执行任务。
示例代码
... private ExecutorService mExecutor = Executors.newSingleThreadExecutor(); ... FutureTask<List<Integer>> task = new FutureTask<List<Integer>>(new MyCallable()) { @Override protected void done() { try { List<Integer> list = get(); //do something... } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } }; mExecutor.execute(task); //或者 //mExecutor.submit(task);
总结
通过这篇文章,我们搞懂了Runnable、Callable、Future之间的关系,以及如何使用线程池执行Runnable、Callable任务并获取执行结果,这对以后我们自己编写框架或者项目开发有着极大的帮助。相关文章推荐
- Java并发编程之——线程池帮助类Executors和Future及Callable相关
- java 多线程(一)---创建线程的三种方式Thread,Runnable,Callable与Future
- 黑马程序员——Java5中的线程并发库(一)---概述、线程池、Callable和Future、Lock和Condition
- Callable、Runnable、Future、Executor的详细使用方法
- Runnable、Callable、Future和FutureTask用法
- Runnable,Callable,Future,RunnableFuture,FutureTask,ExecutorService的关系
- Runnable、Callable、Executor、Future、FutureTask关系解读
- Java:多线程,线程池,使用CompletionService通过Future来处理Callable的返回结果
- Java:多线程,线程池,使用CompletionService通过Future来处理Callable的返回结果
- 多线程 Callable Runnable 与Future
- Java中的Runnable、Callable、Future、FutureTask的区别与示例
- Java中的Runnable、Callable、Future、FutureTask的区别与示例
- Runnable、Callable、Executor、Future、FutureTask关系解读
- Runnable、Callable、Executor、Future、FutureTask关系解读
- Java并发编程之线程池、Callable和Future使用
- Android(Java)之多线程结果返回——Future 、FutureTask、Callable、Runnable
- JAVA线程池之ExecutorService Future Callable Submit简单应用
- Runnable、Callable、Executor、Future、FutureTask关系解读
- JAVA多线程实现的三种方式(继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程)
- java并发编程--Runnable Callable及Future