java多线程基本概述(十三)——Executor
1:Executor接口
public interface Executor 执行已提交的 Runnable 任务的对象。此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节、调度等)分离开来的方法。通常使用 Executor 而不是显式地创建线程。例如,可能会使用以下方法,而不是为一组任务中的每个任务调用 new Thread(new(RunnableTask())).start(): Executor executor = anExecutor; executor.execute(new RunnableTask1()); executor.execute(new RunnableTask2()); ... 不过,Executor 接口并没有严格地要求执行是异步的。在最简单的情况下,执行程序可以在调用者的线程中立即运行已提交的任务: class DirectExecutor implements Executor { public void execute(Runnable r) { r.run(); } } 更常见的是,任务是在某个不是调用者线程的线程中执行的。以下执行程序将为每个任务生成一个新线程。 class ThreadPerTaskExecutor implements Executor { public void execute(Runnable r) { new Thread(r).start(); } } 许多 Executor 实现都对调度任务的方式和时间强加了某种限制。以下执行程序使任务提交与第二个执行程序保持连续,这说明了一个复合执行程序。 class SerialExecutor implements Executor { final Queue<Runnable> tasks = new ArrayDeque<Runnable>(); final Executor executor; Runnable active; SerialExecutor(Executor executor) { this.executor = executor; } public synchronized void execute(final Runnable r) { tasks.offer(new Runnable() { public void run() { try { r.run(); } finally { scheduleNext(); } } }); if (active == null) { scheduleNext(); } } protected synchronized void scheduleNext() { if ((active = tasks.poll()) != null) { executor.execute(active); } } } 此包中提供的 Executor 实现实现了 ExecutorService,这是一个使用更广泛的接口。ThreadPoolExecutor 类提供一个可扩展的线程池实现。Executors 类为这些 Executor 提供了便捷的工厂方法。
2:ExecutorService接口
public interface ExecutorService extends Executor Executor 提供了管理终止的方法,以及可为跟踪一个或多个异步任务执行状况而生成 Future 的方法。 可以关闭 ExecutorService,这将导致其拒绝新任务。提供两个方法来关闭 ExecutorService。
shutdown() 方法在终止前允许执行以前提交的任务,但并不保证之前提交的任务完成。
shutdownNow() 方法阻止等待任务启动并试图停止当前正在执行的任务。
在终止时,执行程序没有任务在执行,也没有任务在等待执行,并且无法提交新任务。
应该关闭未使用的 ExecutorService 以允许回收其资源。 通过创建并返回一个可用于取消执行和/或等待完成的 Future,方法 submit 扩展了基本方法 Executor.execute(java.lang.Runnable)。
方法 invokeAny 和 invokeAll 是批量执行的最常用形式,它们执行任务 collection,然后等待至少一个,或全部任务完成(可使用 ExecutorCompletionService 类来编写这些方法的自定义变体)。 Executors 类提供了用于此包中所提供的执行程序服务的工厂方法。 用法示例 下面给出了一个网络服务的简单结构,这里线程池中的线程作为传入的请求。它使用了预先配置的 Executors.newFixedThreadPool(int) 工厂方法: class NetworkService implements Runnable { private final ServerSocket serverSocket; private final ExecutorService pool; public NetworkService(int port, int poolSize) throws IOException { serverSocket = new ServerSocket(port); pool = Executors.newFixedThreadPool(poolSize); } public void run() { // run the service try { for (;;) { pool.execute(new Handler(serverSocket.accept())); } } catch (IOException ex) { pool.shutdown(); } } } class Handler implements Runnable { private final Socket socket; Handler(Socket socket) { this.socket = socket; } public void run() { // read and service request on socket } } 下列方法分两个阶段关闭 ExecutorService。第一阶段调用 shutdown 拒绝传入任务,然后调用 shutdownNow(如有必要)取消所有遗留的任务: void shutdownAndAwaitTermination(ExecutorService pool) { pool.shutdown(); // Disable new tasks from being submitted try { // Wait a while for existing tasks to terminate if (!pool.awaitTermination(60, TimeUnit.SECONDS)) { pool.shutdownNow(); // Cancel currently executing tasks // Wait a while for tasks to respond to being cancelled if (!pool.awaitTermination(60, TimeUnit.SECONDS)) System.err.println("Pool did not terminate"); } } catch (InterruptedException ie) { // (Re-)Cancel if current thread also interrupted pool.shutdownNow(); // Preserve interrupt status Thread.currentThread().interrupt(); } }
方法api:
void shutdown() Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted.
开始执行一次顺序关闭之前提交的任务,不会再接收新的任务
Invocation has no additional effect if already shut down.
如果已经关闭了,那么调用它不会产生额外的影响 This method does not wait for previously submitted tasks to complete execution. Use awaitTermination to do that.
该方法并不等待之前提交的任务的任务完成,请使用 awaitTermination()方法。
List<Runnable> shutdownNow() Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution.
尝试停止所有正在执行的任务,停止处理处于等待状态的任务。并且返回一个等待执行的任务的list. This method does not wait for actively executing tasks to terminate. Use awaitTermination to do that. 该方法并不会等待正在执行的任务被终止。请使用 awaitTermination()方法。 There are no guarantees beyond best-effort attempts to stop processing actively executing tasks. For example, typical implementations will cancel via Thread.interrupt(), so any task that fails to respond to interrupts may never terminate. 无法保证能够停止正在处理的活动执行任务,但是会尽力尝试。例如,通过 Thread.interrupt() 来取消典型的实现,所以任何任务无法响应中断都可能永远无法终止。 Returns: list of tasks that never commenced execution
boolean isShutdown() Returns true if this executor has been shut down. Returns: true if this executor has been shut down
boolean isTerminated() Returns true if all tasks have completed following shut down. Note that isTerminated is never true unless either shutdown or shutdownNow was called first.
如果关闭后所有任务都已完成,则返回 true。注意,除非首先调用 shutdown 或 shutdownNow,否则 isTerminated 永不为 true。 Returns: true if all tasks have completed following shut down
boolean awaitTermination(long timeout,TimeUnit unit) throws InterruptedException Blocks until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.
如果所有的任务都再shutdown调用后已完成执行,或者超过超时等待时间,或者当前线程被中断,以上几个只要任意一个没有发生都会处于阻塞状态。个人理解,相当于join(long timeout,TimeUnit init).
也就是会使池中的线程执行完后再执行awaitTermination()后的代码
Parameters: timeout - the maximum time to wait unit - the time unit of the timeout argument Returns: true if this executor terminated and false if the timeout elapsed before termination Throws: InterruptedException - if interrupted while waiting
下面通过实例来理解上述方法:
import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; class Task implements Callable<String> { @Override public String call() throws Exception { System.out.println("Task "+Thread.currentThread().getName()); String base = "abcdefghijklmnopqrstuvwxyz0123456789"; Random random = new Random(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < 10; i++) { int number = random.nextInt(base.length()); sb.append(base.charAt(number)); } return sb.toString(); } } class LongTask implements Callable<String> { @Override public String call() throws Exception { System.out.println("LongTask "+Thread.currentThread().getName()); TimeUnit.SECONDS.sleep(5); return "success"; } } public class Test { public static void main(String[] args) { ExecutorService service=null; try { service = Executors.newCachedThreadPool(); service.submit(new Task()); service.submit(new Task()); service.submit(new LongTask()); service.submit(new Task()); System.out.println("isShutdown()="+service.isShutdown()+" isTerminated()="+service.isTerminated()+" before shutdown "); service.shutdown();//调用shutdown()之前的都返回false. System.out.println("isShutdown()="+service.isShutdown()+" isTerminated()="+service.isTerminated()+" after shutdown "); //service.submit(new Task()); //line 1 } catch (Exception e) { e.printStackTrace(); } } }
输出结果:
Task pool-1-thread-2 Task pool-1-thread-1 isShutdown()=false isTerminated()=false before shutdown LongTask pool-1-thread-3 Task pool-1-thread-4 isShutdown()=true isTerminated()=false after shutdown Process finished with exit code 0
说明的第一个问题是:isShutdown()方法仅仅只是判断ExecutorService是否已经调用或shutDown()方法,如果是就返回true,否则返回false.而isTerminated()方法只有再调用shutdown()或shutDownNow()之后才有可能返回true,否则一致返回false.如果取消1 line处的注释,那么会抛异常,因为调用shutdown()方法后不会再接收新的任务。取消注释后代码的运行结果如下:
isShutdown()=false isTerminated()=false before shutdown java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@6d6f6e28 rejected from java.util.concurrent.ThreadPoolExecutor@135fbaa4[Shutting down, pool size = 4, active threads = 4, queued tasks = 0, completed tasks = 0] Task pool-1-thread-1 at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047) isShutdown()=true isTerminated()=false after shutdown at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823) Task pool-1-thread-2 LongTask pool-1-thread-3 at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369) Task pool-1-thread-4 at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134) at Test.main(Test.java:41) Process finished with exit code 0
下面修改代码如下:
public class Test { public static void main(String[] args) { try { ExecutorService service = Executors.newFixedThreadPool(4); service.submit(new Task()); service.submit(new Task()); service.submit(new LongTask()); service.submit(new Task()); System.out.println("isShutdown()="+service.isShutdown()+" isTerminated()="+service.isTerminated()+" before shutdown "); service.shutdown(); int i=1; while (!service.awaitTermination(1, TimeUnit.SECONDS)) {//此处1秒的时间间隔内处于阻塞状态 System.out.println("isShutdown()="+service.isShutdown()+" isTerminated()="+service.isTerminated()+" inner "+(i++)); } System.out.println("isShutdown()="+service.isShutdown()+" isTerminated()="+service.isTerminated()+" outer "+(i)); } catch (Exception e) { e.printStackTrace(); } } }
输出结果:
isShutdown()=false isTerminated()=false before shutdown Task pool-1-thread-1 Task pool-1-thread-2 LongTask pool-1-thread-3 Task pool-1-thread-4 isShutdown()=true isTerminated()=false inner 1 isShutdown()=true isTerminated()=false inner 2 isShutdown()=true isTerminated()=false inner 3 isShutdown()=true isTerminated()=false inner 4 isShutdown()=true isTerminated()=true outer 5
service.awaitTermination(1, TimeUnit.SECONDS) 每1秒检查一次executorService是否被关闭。而LongTask正好需要执行5秒,因此会在前4秒监测时ExecutorService为未关闭状态,而在第5秒时
已经关闭,因此第5秒时输出: isShutdown()=true isTerminated()=true outer 5 。接续改代码如下:使await()的超时时间超过longTask的执行时间。
public class Test { public static void main(String[] args) { try { ExecutorService service = Executors.newFixedThreadPool(4); service.submit(new Task()); service.submit(new Task()); service.submit(new LongTask()); service.submit(new Task()); System.out.println("isShutdown()="+service.isShutdown()+" isTerminated()="+service.isTerminated()+" before shutdown "); service.shutdown(); int i=1; System.out.println("before awaitTermination currentTime "+System.currentTimeMillis()); while (!service.awaitTermination(11, TimeUnit.SECONDS)) { System.out.println("isShutdown()="+service.isShutdown()+" isTerminated()="+service.isTerminated()+" inner "+(i++)); //2 line } System.out.println("after awaitTermination currentTime "+System.currentTimeMillis()); System.out.println("isShutdown()="+service.isShutdown()+" isTerminated()="+service.isTerminated()+" outer "+(i)); } catch (Exception e) { e.printStackTrace(); } } }
输出结果:
Task pool-1-thread-1 isShutdown()=false isTerminated()=false before shutdown Task pool-1-thread-2 before awaitTermination currentTime 1492589643688 //调用await前的时间戳为1492589643688 Task pool-1-thread-4 LongTask pool-1-thread-3 after awaitTermination currentTime 1492589648689 //调用await后的时间戳为 1492589648689 二者之间的时间差是5秒,而2 line没有输出,说明其处于阻塞状态。因为5秒后,longTask执行完 isShutdown()=true isTerminated()=true outer 1 //就直接打印了这句话,说明再超时等待时间内完成了任务。 Process finished with exit code 0
如果代码改为下面的:
import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; class Task implements Callable<String> { @Override public String call() throws Exception { System.out.println("Task "+Thread.currentThread().getName()); String base = "abcdefghijklmnopqrstuvwxyz0123456789"; Random random = new Random(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < 10; i++) { int number = random.nextInt(base.length()); sb.append(base.charAt(number)); } return sb.toString(); } } class LongTask implements Callable<String> { @Override public String call() throws Exception { System.out.println("LongTask "+Thread.currentThread().getName()); TimeUnit.SECONDS.sleep(Long.MAX_VALUE); return "success"; } } public class Test { public static void main(String[] args) { try { ExecutorService service = Executors.newFixedThreadPool(4); service.submit(new Task()); service.submit(new Task()); service.submit(new LongTask()); service.submit(new Task()); service.shutdown(); int i=1; System.out.println("before awaitTermination currentTime "+System.currentTimeMillis()); while (!service.awaitTermination(1, TimeUnit.MILLISECONDS)) { /* System.out.println("isShutdown()="+service.isShutdown()+" isTerminated()="+service.isTerminated()+" inner "+(i++)); if (i==10000){ service.shutdownNow(); }*/ } System.out.println("after awaitTermination currentTime "+System.currentTimeMillis()); System.out.println("isShutdown()="+service.isShutdown()+" isTerminated()="+service.isTerminated()+" outer "+(i)); } catch (Exception e) { e.printStackTrace(); } } }
输出结果:
isShutdown()=true isTerminated()=false inner 9993 isShutdown()=true isTerminated()=false inner 9994 isShutdown()=true isTerminated()=false inner 9995 isShutdown()=true isTerminated()=false inner 9996 isShutdown()=true isTerminated()=false inner 9997 isShutdown()=true isTerminated()=false inner 9998 isShutdown()=true isTerminated()=false inner 9999 ..............
.............
..........
基本处于无限等待。而api描述说的是 “shutdown() 方法在终止前允许执行以前提交的任务,但并不保证之前提交的任务完成 “,而现在似乎一直再等待线程池中的任务完成。不知道如何验证、、、
下面更改代码,使其子线程结束
public class Test { public static void main(String[] args) { try { ExecutorService service = Executors.newFixedThreadPool(4); LongTask t = new LongTask(); service.submit(new Task()); service.submit(new Task()); service.submit(t); service.submit(new Task()); service.shutdown(); int i=1; System.out.println("before awaitTermination currentTime "+System.currentTimeMillis()); while (!service.awaitTermination(1, TimeUnit.MILLISECONDS)) { System.out.println("isShutdown()="+service.isShutdown()+" isTerminated()="+service.isTerminated()+" inner "+(i++)); if (i==10000){ service.shutdownNow(); } } System.out.println("after awaitTermination currentTime "+System.currentTimeMillis()); System.out.println("isShutdown()="+service.isShutdown()+" isTerminated()="+service.isTerminated()+" outer "+(i)); } catch (Exception e) { e.printStackTrace(); } } }
输出结果:
....................... ....................... ....................... ....................... isShutdown()=true isTerminated()=false inner 9993 isShutdown()=true isTerminated()=false inner 9994 isShutdown()=true isTerminated()=false inner 9995 isShutdown()=true isTerminated()=false inner 9996 isShutdown()=true isTerminated()=false inner 9997 isShutdown()=true isTerminated()=false inner 9998 isShutdown()=true isTerminated()=false inner 9999 after awaitTermination currentTime 1492591173801 isShutdown()=true isTerminated()=true outer 10000 Process finished with exit code 0
可知再i=10000的时候,调用shutdownNow()后,子线程被结束。api描述是 “该方法并不会等待正在执行的任务被终止,无法保证能够停止正在处理的活动执行任务,但是会尽力尝试。“
但现在似乎验证的场景又再次说明了调用该方法,又会停止线程池中正在执行的任务。不知道如何验证、、、
继续更改代码
import java.util.List; import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; class Task implements Callable<String> { @Override public String call() throws Exception { System.out.println("Task "+Thread.currentThread().getName()); String base = "abcdefghijklmnopqrstuvwxyz0123456789"; Random random = new Random(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < 10; i++) { int number = random.nextInt(base.length()); sb.append(base.charAt(number)); } return sb.toString(); } } class LongTask implements Callable<String> { @Override public String call() throws Exception { System.out.println("LongTask "+Thread.currentThread().getName()); TimeUnit.SECONDS.sleep(10); return "success"; } } public class Test { public static void main(String[] args) { try { ExecutorService service = Executors.newFixedThreadPool(4); service.submit(new LongTask()); service.submit(new LongTask()); service.submit(new LongTask()); service.submit(new LongTask()); service.submit(new LongTask()); service.submit(new LongTask()); List<Runnable> runnables = service.shutdownNow(); System.out.println(runnables.size()); int i=1; while (!service.awaitTermination(1, TimeUnit.MILLISECONDS)) { System.out.println("isShutdown()="+service.isShutdown()+" isTerminated()="+service.isTerminated()+" inner "+(i++)); } } catch (Exception e) { e.printStackTrace(); } } }
输出结果:
LongTask pool-1-thread-1 LongTask pool-1-thread-2 2 LongTask pool-1-thread-3 LongTask pool-1-thread-4 Process finished with exit code 0
我们限制了线程池的长度是4,提交了6个任务,这样将有两个任务在工作队列中等待,当我们执行shutdownNow方法时,ExecutorService被立刻关闭,所以在service.awaitTermination(1, TimeUnit.MILLISECONDS)方法校验时返回的是false,因此没有输出。而在调用shutdownNow方法时,我们接受到了一个List,这里包含的是在工作队列中等待执行的任务,由于线程池长度为4,且执行的都是长任务,所以当提交了三个任务后线程池已经满了,剩下的两次提交只能在工作队列中等待,因此我们看到runnables的大小为2.