ExecutorService
java并发中ExecutorService的使用
ExecutorService是java中的一个异步执行的框架,通过使用ExecutorService可以方便的创建多线程执行环境。
本文将会详细的讲解ExecutorService的具体使用。
创建ExecutorService
Executors 提供了一系列工厂方法用于创先线程池,返回的线程池都实现了 ExecutorService 接口。
/* * 该方法返回一个固定线程数量的线程池,该线程池池中的线程数量始终不变。 * 当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。 * 若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务 * 默认等待队列长度为Integer.MAX_VALUE */ ExecutorService fixedThreadPool = Executors.newFixedThreadPool(1); /* * 该方法返回一个只有一个线程的线程池。 * 若多余一个任务被提交到线程池,任务会被保存在一个任务队列中,等待线程空闲,按先入先出顺序执行队列中的任务 * 默认等待队列长度为Integer.MAX_VALUE */ ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); /* * 该方法返回一个可根据实际情况调整线程数量的线程池。 * 线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。 * 若所有线程均在工作,又有新任务的提交,则会创建新的线程处理任务。 * 所有线程在当前任务执行完毕后,将返回线程池进行复用 */ ExecutorService newCachedThreadPool = Executors.newCachedThreadPool(); /* * 该方法返回一个ScheduledExecutorService对象,线程池大小为1。 * ScheduledExecutorService接口在ExecutorService接口之上扩展了在给定时间内执行某任务的功能, * 如在某个固定的延时之后执行,或者周期性执行某个任务 */ ExecutorService newSingleThreadScheduledExecutor = Executors.newSingleThreadScheduledExecutor(); /* * 该方法也返回一个ScheduledExecutorService对象,但该线程池可以指定线程数量 */ ExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(1);
ExecutorService 使用方法
这里有几种不同的方式让你将任务委托给壹個 ExecutorService。- execute(Runnable)
executorService.execute(new Runnable() { public void run() { System.out.println("Asynchronous task"); } });
- Future submit(Runnable)
Future future = executorService.submit(new Runnable() { public void run() { System.out.println("Asynchronous task"); } }); // 如果任务结束执行则返回 null System.out.println("future.get()=" + future.get());
- Future submit(Callable)
Callable 的 call() 方法可以返回一个结果。Callable 的返回值可以从方法 submit(Callable) 返回的 Future 对象中获取。
Future future = executorService.submit(new Callable(){ public Object call() throws Exception { System.out.println("Asynchronous Callable"); return "Callable Result"; } }); System.out.println("future.get() = " + future.get());
- T invokeAny(Collection<Callable>)
任务集合中,任何一个任务完成就返回。
接收Callable 对象的集合作为参数。调用该方法不会返回 Future 对象,而是返回集合中某一個 Callable 对象的结果。
无法保证调用之后返回的结果是哪個 Callable。
ExecutorService executorService = Executors.newSingleThreadExecutor(); Set<Callable<String>> callables = new HashSet<Callable<String>>(); callables.add(new Callable<String>() { public String call() throws Exception { return "Task 1"; } }); callables.add(new Callable<String>() { public String call() throws Exception { return "Task 2"; } }); callables.add(new Callable<String>() { public String call() throws Exception { return "Task 3"; } }); String result = executorService.invokeAny(callables); System.out.println("result = " + result); executorService.shutdown();
- List<Future> invokeAll(Collection<Callable>)
同步的方法,执行所有的任务列表,当所有任务都执行完成后,返回Future列表。
执行Runnable任务
public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(3); for (int i = 0; i < 5; i++) { int finalI = i; executorService.execute(new Runnable() { @Override public void run() { try { // 休眠is TimeUnit.SECONDS.sleep(finalI); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "线程被调用了。" + new Date()); } }); } }
执行结果如下:
pool-1-thread-1线程被调用了。Fri Aug 21 14:47:33 CST 2020 pool-1-thread-2线程被调用了。Fri Aug 21 14:47:34 CST 2020 pool-1-thread-3线程被调用了。Fri Aug 21 14:47:35 CST 2020 pool-1-thread-1线程被调用了。Fri Aug 21 14:47:36 CST 2020 pool-1-thread-2线程被调用了。Fri Aug 21 14:47:38 CST 2020
从结果中可以看出,pool-1-thread-1和pool-1-thread-2均被调用两次,我们设定的线程数量为3个,如果线程池中没有空闲的线程,它会等待之前运行的线程结束后再执行任务
ExecutorService 服务关闭
shutdown()
、shutdownNow()
、awaitTermination()
)shutdown()
将线程池状态置为 SHUTDOWN
,并不会立即停止:
- 停止接收外部submit的任务
- 内部正在跑的任务和队列里等待的任务,会执行完
- 等到第二步完成后,才真正停止
shutdownNow()
将线程池状态置为STOP
。企图立即停止,事实上不一定:
- 跟shutdown()一样,先停止接收外部提交的任务
- 忽略队列里等待的任务
- 尝试将正在跑的任务
interrupt
中断 - 返回未执行的任务列表
它试图终止线程的方法是通过调用
Thread.interrupt()
方法来实现的,但是大家知道,这种方法的作用有限,如果线程中没有sleep 、wait、Condition、定时锁等应用, interrupt()方法是无法中断当前的线程的。所以,ShutdownNow()并不代表线程池就一定立即就能退出,它也可能必须要等待所有正在执行的任务都执行完成了才能退出。但是大多数时候是能立即退出的
awaitTermination(long timeOut, TimeUnit unit)
当前线程阻塞,直到:
- 等所有已提交的任务(包括正在跑的和队列中等待的)执行完;
- 或者 等超时时间到了(timeout 和 TimeUnit设定的时间);
- 或者 线程被中断,抛出InterruptedException
一般配合 shutdown() 使用
1、shutdown() 和 shutdownNow() 的区别
shutdown()
只是关闭了提交通道,用submit()是无效的;而内部该怎么跑还是怎么跑,跑完再停。shutdownNow()
能立即停止线程池,正在跑的和正在等待的任务都停下了。2、shutdown() 和 awaitTermination() 的区别
shutdown()
后,不能再提交新的任务进去;但是 awaitTermination()
后,可以继续提交。
awaitTermination()
是阻塞的,返回结果是线程池是否已停止(true/false);shutdown()
不阻塞。