- submit方法
// submit方法1: 参数为任务方法体Runnable public Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); RunnableFuture<Void> ftask = newTaskFor(task, null); execute(ftask); return ftask; } // submit方法2: 参数为任务方法体Runnable, 结果T public <T> Future<T> submit(Runnable task, T result) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task, result); execute(ftask); return ftask; } // submit方法3: 参数为任务方法体Callable public <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task); execute(ftask); return ftask; } /** * 执行逻辑: * 都是创建的newTaskFor(FutureTask) 将任务传递到FutureTask中, 来处理任务的。 * 调用execute方法执行任务。 * 返回结果。 */ /** * 区别: * 区别主要在参数与获取返回值的方式。 * 参数包含Runnable都是通过newTaskFor(task, null);创建任务FutureTask, 第一个参数时Runnable, 第二个参数是其返回的结果。 * 参数是Callable是通过newTaskFor(Callable);创建的任务FutureTask。 * 无论那种方式, 其实际都是创建了一个带有Callable属性的FutureTask任务对象。通过FutureTask来执行具体的任务, 实际执行逻辑就是Callable的call方法。 * 如果参数是Callable, 那么创建的FutureTask的Callable属性就是参数Callable对象; 如果参数是Runnable那么就会通过Executors.callable(runnable, result); * 来构建Callable对象, 通过构建的Callable来创建FutureTask对象。 * 所以三种方法的返回值也比较清楚了, 方法1, 只有Runnable, 没有传入返回值, 通过传入null作为结果, 构建Callable, 所以通过submit返回值Future对象的get方法获取返回值时, * 返回的就是null, 无论run方法体是如何执行的。方法2, 参数为Runnable和T result, 那么Callable的call方法就是Runnable的run方法, Callable的返回值就是T result。也就是说, * 先执行任务逻辑run方法, 再直接将T result作为返回值返回。所以无论run方法如何执行, 返回的都是result。所以调用get方法获取返回值时,返回的就是result。 * 方法1和方法2类似, 都是将第二个参数作为返回值。方法3, 直接传入的Callable, 直接使用其创建FutureTask。那么任务逻辑就是call方法, 返回值就是Callable的返回值。 */ /** * Runnable参数时, 如何构建Callable对象: * submit中使用newTaskFor创建一个FutureTask对象。 * 如果传入的是Callable那么调用FutureTask的构造方法new FutureTask(Callable)创建。 * 如果传入的是Runnable那么通过调用FutureTask的构造方法FutureTask<T>(runnable, value);该构造方法通过Executors.callable(runnable, result);创建Callable * 作为FutureTask的Callable属性。Executors.callable(runnable, result);创建内部类RunnableAdapter, 将Runnable转换成Callable。 */
-
invokeAll方法:
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException { if (tasks == null) throw new NullPointerException(); // 结果集合, 存放所有任务的结果。越先执行完成的任务, 结果越往前。 ArrayList<Future<T>> futures = new ArrayList<Future<T>>(tasks.size()); // 所有任务是否全部完成 boolean done = false; try { for (Callable<T> t : tasks) { // 创建任务FutureTask(实现RunnableFuture),并且执行任务 RunnableFuture<T> f = newTaskFor(t); futures.add(f); // 调用父类ExecutorService的方法,具体执行逻辑由子类实现 execute(f); } // 遍历结果队列,判断是否所有任务全部成功执行 for (int i = 0, size = futures.size(); i < size; i++) { Future<T> f = futures.get(i); if (!f.isDone()) { try { // 等待任务执行完成。 f.get(); } catch (CancellationException ignore) { } catch (ExecutionException ignore) { } } } done = true; return futures; } finally { // 如果出现异常,导致没有全部执行成功,取消未执行完成的任务。 if (!done) for (int i = 0, size = futures.size(); i < size; i++) futures.get(i).cancel(true); } }
-
invokeAll方法(带超时时间):
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException { if (tasks == null) throw new NullPointerException(); long nanos = unit.toNanos(timeout); // 创建任务队列,Future集合 ArrayList<Future<T>> futures = new ArrayList<Future<T>>(tasks.size()); boolean done = false; try { // 先将所有任务转换成FutureTask(实现了RunnableFuture),并且添加到任务队列中。 // 将添加任务队列操作和只执行操作分开的原因是因为是纳秒级别的,所有new,add等操作也是耗费时间的, // 分开操作是为了更精准的计算超时时间。 for (Callable<T> t : tasks) futures.add(newTaskFor(t)); // 计算出超时时间,当前纳秒 + 超时时间时间 final long deadline = System.nanoTime() + nanos; final int size = futures.size(); // Interleave time checks and calls to execute in case // executor doesn't have any/much parallelism. for (int i = 0; i < size; i++) { // 执行所有任务 execute((Runnable)futures.get(i)); // 如果当前时间 > 超时时间 那么终止执行,返回结果。 // 如果超时时间较短,任务多,那么可能存在某些任务未执行 nanos = deadline - System.nanoTime(); if (nanos <= 0L) return futures; } for (int i = 0; i < size; i++) { Future<T> f = futures.get(i); if (!f.isDone()) { if (nanos <= 0L) return futures; try { // 等待任务执行完成,并计算执行时间 f.get(nanos, TimeUnit.NANOSECONDS); } catch (CancellationException ignore) { } catch (ExecutionException ignore) { } catch (TimeoutException toe) { return futures; } // 计算是否超时 nanos = deadline - System.nanoTime(); } } done = true; return futures; } finally { if (!done) for (int i = 0, size = futures.size(); i < size; i++) futures.get(i).cancel(true); } }
-
invokeAny
// 某个任务执行完成后返回结果 public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException { try { return doInvokeAny(tasks, false, 0); } catch (TimeoutException cannotHappen) { assert false; return null; } } // 带有超时时间, 某个任务执行完成后返回结果 public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return doInvokeAny(tasks, true, unit.toNanos(timeout)); }
-
doInvokeAny方法
private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks, boolean timed, long nanos) throws InterruptedException, ExecutionException, TimeoutException { // 返回第一个完成的任务的执行结果。 if (tasks == null) throw new NullPointerException(); int ntasks = tasks.size(); if (ntasks == 0) throw new IllegalArgumentException(); ArrayList<Future<T>> futures = new ArrayList<Future<T>>(ntasks); /** * 这里使用ExecutorCompletionService的作用。 * 代理线程池去执行任务,当任务执行完后,将执行结果加入到队列中,供外部判断是否存在任务已完成。 * 首先,ExecutorCompletionService是用来提交执行任务的。ExecutorCompletionService中定义了一个Executor, * 他用来执行一个任务,具体的执行逻辑由子类实现。执行逻辑,将任务提交(submit)给ExecutorCompletionService(简称ecs), * 首先ecs会将任务进行封装,封装成ecs的内部类QueueingFuture,QueueingFuture继承了FutureTask,使任务完成具有返回值, * 并且QueueingFuture重写了FutureTask的done方法,done方法是一个回调方法,也就是说,当任务执行完成时,会自动调用done方法, * 而QueueingFuture重写done方法,在任务执行完成时将任务的返回值Future加入队列BlockingQueue(ExecutorCompletionService * 定义的一个属性)。没完成一个任务就会加入一个结果Future到BlockingQueue中,顺序就是任务完成时的顺序。那么在doInvokeAny方法 * 中,死循环中不断判断是否有任务已经完成,只要调用ExecutorCompletionService的poll方法即可。poll方法就是从BlockingQueue中 * 取出第一个元素,如果取到,那么证明有任务完成,那么doInvokeAny就可以返回了(完成了任意一个任务)。如果返回的是null那么证明 * 还没有任务执行完成,继续循环判断。其中poll方法是调用BlockingQueue的poll方法,在ExecutorCompletionService中 * BlockingQueue是通过LinkedBlockingQueue实现的。 */ ExecutorCompletionService<T> ecs = new ExecutorCompletionService<T>(this); // For efficiency, especially in executors with limited // parallelism, check to see if previously submitted tasks are // done before submitting more of them. This interleaving // plus the exception mechanics account for messiness of main // loop. try { // Record exceptions so that if we fail to obtain any // result, we can throw the last exception we got. ExecutionException ee = null; // 计算超时时间 final long deadline = timed ? System.nanoTime() + nanos : 0L; // 任务队列的迭代器, 遍历所有任务 Iterator<? extends Callable<T>> it = tasks.iterator(); // Start one task for sure; the rest incrementally // 将第一个任务提交, 并将结果加入到futures结果集中。其余的后续添加。 // 由于CPU执行效率非常高, 当添加多个任务执行时, 可能会存在并发执行多个任务的时候, 但是返回值只会是最先执行完成的任务。 // 其他任务会调cancel方法, 取消其他任务。 futures.add(ecs.submit(it.next())); // 任务数量-1 --ntasks; // 当前正在执行的任务数量 int active = 1; // 循环等待是否有任务完成 for (;;) { // 从结果列表中获取头结点 Future<T> f = ecs.poll(); // 头结点对应的结果==null, 表示还没有任务完成 if (f == null) { // 剩余的任务数量>0 if (ntasks > 0) { // 继续执行后续任务, 并将结果添加到futures结果集中。 --ntasks; futures.add(ecs.submit(it.next())); ++active; } // 如果当前执行的任务数量==0, 表示有任务已经完成 else if (active == 0) break; // 是否存在超时时间 else if (timed) { // 在超时时间范围内获取结果 f = ecs.poll(nanos, TimeUnit.NANOSECONDS); if (f == null) throw new TimeoutException(); // 计算剩余的超时时间 nanos = deadline - System.nanoTime(); } else // 所有任务都提交执行了,并且没有一个执行完,并且没有超时时间,则使用take方法一直等待 // 执行结果,而不是使用poll方法。 // poll和take方法的却别:poll方法如果队列中没有数据直接返回null,不会等待,如果有则返回 // 队头节点。take方法也是获取队头节点,区别在于如果队列中没有元素,则会一直等待,直到队列非空。 // take方法使用的是await没有超时时间所以会一直等待结果的出现。而poll方法,如果没有设置超时时间,则不会等待, // 有就返回,没有就返回null,如果设置了超时时间,则使用awaitNanos方法进行等待,如果在超时时间范围内返回结果, // 那么就直接返回结果,如果超过超时时间就抛出TimeoutException异常。 f = ecs.take(); } if (f != null) { --active; try { return f.get(); } catch (ExecutionException eex) { ee = eex; } catch (RuntimeException rex) { ee = new ExecutionException(rex); } } } if (ee == null) ee = new ExecutionException(); throw ee; } finally { for (int i = 0, size = futures.size(); i < size; i++) // 实际并不能完全中断任务线程,只是通过调用线程的interrupt方法设置打断标志。让任务线程中包含sleep,wait,join等阻塞逻辑的线程抛出异常,从而中断线程执行。 // FutureTask cancel方法,尝试取消任务的执行。如果任务已完成或以取消返回false。 // 只有是new状态的任务才能取消,如果是其他状态则不能取消,返回false futures.get(i).cancel(true); } }