ExecutorService中submit和execute、Runnable和Callable
ExecutorService是Java中对线程池定义的一个接口,它java.util.concurrent包中,在这个接口中定义了和后台任务执行相关的方法。
Java API对ExecutorService接口的实现有两个,所以这两个即是Java线程池具体实现类:
- ThreadPoolExecutor
- ScheduledThreadPoolExecutor
1、创建ExecutorService
通过工具类java.util.concurrent.Executors的静态方法来创建。
Executors包中有 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 类的工厂和实用方法。
比如,创建一个ExecutorService的实例,ExecutorService实际上是一个线程池的管理工具:
ExecutorService executorService = Executors.newCachedThreadPool();
ExecutorService executorService = Executors.newFixedThreadPool(3);
ExecutorService executorService = Executors.newSingleThreadExecutor();
2、创建任务
任务就是一个实现了Runnable或Callable接口的类。
// Callable接口
public interface Callable<V> {
V call() throws Exception;
}
// Runnable接口
public interface Runnable {
public abstract void run();
}
不同点
-
Runnable没有返回值;Callable可以返回执行结果,是个泛型,和Future、FutureTask配合可以用来获取异步执行的结果
-
Callable接口的call()方法允许抛出异常;Runnable的run()方法异常只能在内部消化,不能往上继续抛
-
Callalble接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。
3、将任务添加到线程去执行
调用execute或submit,都可以将一个任务添加到线程池中。
线程池会为每个任务创建一个线程,该线程会在之后的某个时刻自动执行。
ExecutorService接口,继承Executor接口。
public interface ExecutorService extends Executor {
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
}
public interface Executor {
void execute(Runnable command);
}
submit和execute的区别:
-
submit有返回值,而execute没有
用到返回值的例子,比如说有很多个做数据验证的task,希望所有的task执行完,然后每个task告诉我它的执行结果,如果失败,原因是什么,然后把所有失败的原因综合起来返回。 -
submit方便Exception处理
如果想在外边能够感知到task内部的exception,就需只能用submit,并捕获Future.get抛出的异常。
比如说有很多更新数据的task,希望如果其中一个task失败,其它的task就不需要执行了。那就需要catch Future.get抛出的异常,然后终止其它task的执行。
同样submit方法,参数Runnable和Callable也稍微有些不同:
- 当将Callable的对象传递给ExecutorService的submit方法,则该call方法自动在一个线程上执行,并且会返回执行结果Future对象。
- 当将Runnable的对象传递给ExecutorService的submit方法,则该run方法自动在一个线程上执行,并且会返回执行结果Future对象,但是在该Future对象上调用get方法,将返回null。