线程池的深入理解
简单研究下线程池的执行原理。以及execute 和 submit 方法的区别。
1. execute 方法接收的是一个Runnable 参数,返回值是void 类型,也就是不接收结果, 方法签名如下:
java.util.concurrent.Executor#execute
void execute(Runnable command);
2. submit 方法三个重载的方法,如下:
对应的方法如下:‘
(1) java.util.concurrent.ExecutorService#submit(java.lang.Runnable)
Future<?> submit(Runnable task);
(2) java.util.concurrent.ExecutorService#submit(java.lang.Runnable, T)
<T> Future<T> submit(Runnable task, T result);
(3) java.util.concurrent.ExecutorService#submit(java.util.concurrent.Callable<T>)
<T> Future<T> submit(Callable<T> task);
1.java.util.concurrent.ThreadPoolExecutor#execute理解
测试代码
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(3)); // execute 方法无返回值,相当于执行不带返回结果的任务 threadPoolExecutor.execute(() -> { log.info("--" + Thread.currentThread().getName() + "--A"); try { Thread.sleep(2 * 1000); } catch (InterruptedException e) { } log.info("--" + Thread.currentThread().getName() + "--B"); });
查看源代码如下:
(1) 接口是Executor 的方法
void execute(Runnable command);
(2) 实现类java.util.concurrent.ThreadPoolExecutor#execute
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); /* * Proceed in 3 steps: * * 1. If fewer than corePoolSize threads are running, try to * start a new thread with the given command as its first * task. The call to addWorker atomically checks runState and * workerCount, and so prevents false alarms that would add * threads when it shouldn't, by returning false. * * 2. If a task can be successfully queued, then we still need * to double-check whether we should have added a thread * (because existing ones died since last checking) or that * the pool shut down since entry into this method. So we * recheck state and if necessary roll back the enqueuing if * stopped, or start a new thread if there are none. * * 3. If we cannot queue task, then we try to add a new * thread. If it fails, we know we are shut down or saturated * and so reject the task. */ int c = ctl.get(); if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; c = ctl.get(); } if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) reject(command); else if (workerCountOf(recheck) == 0) addWorker(null, false); } else if (!addWorker(command, false)) reject(command); }
源码跟踪:
(1) addWorker(command, true) 增加worker
private boolean addWorker(Runnable firstTask, boolean core) { retry: for (;;) { int c = ctl.get(); int rs = runStateOf(c); // Check if queue empty only if necessary. if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) return false; for (;;) { int wc = workerCountOf(c); if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) return false; if (compareAndIncrementWorkerCount(c)) break retry; c = ctl.get(); // Re-read ctl if (runStateOf(c) != rs) continue retry; // else CAS failed due to workerCount change; retry inner loop } } boolean workerStarted = false; boolean workerAdded = false; Worker w = null; try { w = new Worker(firstTask); final Thread t = w.thread; if (t != null) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { // Recheck while holding lock. // Back out on ThreadFactory failure or if // shut down before lock acquired. int rs = runStateOf(ctl.get()); if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) { if (t.isAlive()) // precheck that t is startable throw new IllegalThreadStateException(); workers.add(w); int s = workers.size(); if (s > largestPoolSize) largestPoolSize = s; workerAdded = true; } } finally { mainLock.unlock(); } if (workerAdded) { t.start(); workerStarted = true; } } } finally { if (! workerStarted) addWorkerFailed(w); } return workerStarted; }
worker 是一个内部类java.util.concurrent.ThreadPoolExecutor.Worker,其包含的重要属性和方法如下:
private final class Worker extends AbstractQueuedSynchronizer implements Runnable { /** * This class will never be serialized, but we provide a * serialVersionUID to suppress a javac warning. */ private static final long serialVersionUID = 6138294804551838833L; /** Thread this worker is running in. Null if factory fails. */ final Thread thread; /** Initial task to run. Possibly null. */ Runnable firstTask; /** Per-thread task counter */ volatile long completedTasks; Worker(Runnable firstTask) { setState(-1); // inhibit interrupts until runWorker this.firstTask = firstTask; this.thread = getThreadFactory().newThread(this); } public void run() { runWorker(this); } final void runWorker(Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; w.firstTask = null; w.unlock(); // allow interrupts boolean completedAbruptly = true; try { while (task != null || (task = getTask()) != null) { w.lock(); // If pool is stopping, ensure thread is interrupted; // if not, ensure thread is not interrupted. This // requires a recheck in second case to deal with // shutdownNow race while clearing interrupt if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted()) wt.interrupt(); try { beforeExecute(wt, task); Throwable thrown = null; try { task.run(); } catch (RuntimeException x) { thrown = x; throw x; } catch (Error x) { thrown = x; throw x; } catch (Throwable x) { thrown = x; throw new Error(x); } finally { afterExecute(task, thrown); } } finally { task = null; w.completedTasks++; w.unlock(); } } completedAbruptly = false; } finally { processWorkerExit(w, completedAbruptly); } } ...
1》new Worker 的时候会创建线程,线程传入的Runnable 对象是Worker 自身,同时Worker
2》t.start(); 启动的时候会调用java.util.concurrent.ThreadPoolExecutor.Worker#run 方法
3》然后调用到java.util.concurrent.ThreadPoolExecutor#runWorker, 方法内部调用task.run(); 相当于同步的调用 传给线程池的Runnable 任务的run 方法。
4》当不是第一个任务的时候走的是java.util.concurrent.ThreadPoolExecutor#getTask, 也就是从任务队列中阻塞拿去任务
private Runnable getTask() { boolean timedOut = false; // Did the last poll() time out? for (;;) { int c = ctl.get(); int rs = runStateOf(c); // Check if queue empty only if necessary. if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { decrementWorkerCount(); return null; } int wc = workerCountOf(c); // Are workers subject to culling? boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { if (compareAndDecrementWorkerCount(c)) return null; continue; } try { Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); if (r != null) return r; timedOut = true; } catch (InterruptedException retry) { timedOut = false; } } }
2. submit理解
1. java.util.concurrent.AbstractExecutorService#submit(java.lang.Runnable) 理解
查看源码如下:java.util.concurrent.AbstractExecutorService#submit(java.lang.Runnable)
public Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); RunnableFuture<Void> ftask = newTaskFor(task, null); execute(ftask); return ftask; }
可以理解大体的思路是对,传给线程池的Runnable 进行了下包装,java.util.concurrent.AbstractExecutorService#newTaskFor(java.lang.Runnable, T) 如下:
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { return new FutureTask<T>(runnable, value); }
(1) java.util.concurrent.RunnableFuture 继承了接口Runnable, Future<V>
public interface RunnableFuture<V> extends Runnable, Future<V> { void run(); }
java.util.concurrent.Future 接口如下:
public interface Future<V> { /** * Attempts to cancel execution of this task. This attempt will * fail if the task has already completed, has already been cancelled, * or could not be cancelled for some other reason. If successful, * and this task has not started when {@code cancel} is called, * this task should never run. If the task has already started, * then the {@code mayInterruptIfRunning} parameter determines * whether the thread executing this task should be interrupted in * an attempt to stop the task. * * <p>After this method returns, subsequent calls to {@link #isDone} will * always return {@code true}. Subsequent calls to {@link #isCancelled} * will always return {@code true} if this method returned {@code true}. * * @param mayInterruptIfRunning {@code true} if the thread executing this * task should be interrupted; otherwise, in-progress tasks are allowed * to complete * @return {@code false} if the task could not be cancelled, * typically because it has already completed normally; * {@code true} otherwise */ boolean cancel(boolean mayInterruptIfRunning); /** * Returns {@code true} if this task was cancelled before it completed * normally. * * @return {@code true} if this task was cancelled before it completed */ boolean isCancelled(); /** * Returns {@code true} if this task completed. * * Completion may be due to normal termination, an exception, or * cancellation -- in all of these cases, this method will return * {@code true}. * * @return {@code true} if this task completed */ boolean isDone(); /** * Waits if necessary for the computation to complete, and then * retrieves its result. * * @return the computed result * @throws CancellationException if the computation was cancelled * @throws ExecutionException if the computation threw an * exception * @throws InterruptedException if the current thread was interrupted * while waiting */ V get() throws InterruptedException, ExecutionException; /** * Waits if necessary for at most the given time for the computation * to complete, and then retrieves its result, if available. * * @param timeout the maximum time to wait * @param unit the time unit of the timeout argument * @return the computed result * @throws CancellationException if the computation was cancelled * @throws ExecutionException if the computation threw an * exception * @throws InterruptedException if the current thread was interrupted * while waiting * @throws TimeoutException if the wait timed out */ V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
java.util.concurrent.FutureTask 的重要属性和方法如下:
public class FutureTask<V> implements RunnableFuture<V> { private volatile int state; private static final int NEW = 0; private static final int COMPLETING = 1; private static final int NORMAL = 2; private static final int EXCEPTIONAL = 3; private static final int CANCELLED = 4; private static final int INTERRUPTING = 5; private static final int INTERRUPTED = 6; /** The underlying callable; nulled out after running */ private Callable<V> callable; /** The result to return or exception to throw from get() */ private Object outcome; // non-volatile, protected by state reads/writes /** The thread running the callable; CASed during run() */ private volatile Thread runner; /** Treiber stack of waiting threads */ private volatile WaitNode waiters; public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable } public FutureTask(Runnable runnable, V result) { this.callable = Executors.callable(runnable, result); this.state = NEW; // ensure visibility of callable } public boolean isCancelled() { return state >= CANCELLED; } public boolean isDone() { return state != NEW; } public boolean cancel(boolean mayInterruptIfRunning) { if (!(state == NEW && UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED))) return false; try { // in case call to interrupt throws exception if (mayInterruptIfRunning) { try { Thread t = runner; if (t != null) t.interrupt(); } finally { // final state UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED); } } } finally { finishCompletion(); } return true; } public V get() throws InterruptedException, ExecutionException { int s = state; if (s <= COMPLETING) s = awaitDone(false, 0L); return report(s); } public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if (unit == null) throw new NullPointerException(); int s = state; if (s <= COMPLETING && (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING) throw new TimeoutException(); return report(s); } protected void done() { } protected void set(V v) { if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { outcome = v; UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state finishCompletion(); } } ...
1》java.util.concurrent.Executors#callable(java.lang.Runnable, T) 采用适配器模式,将Runnable 适配为Callable
public static <T> Callable<T> callable(Runnable task, T result) { if (task == null) throw new NullPointerException(); return new RunnableAdapter<T>(task, result); }
java.util.concurrent.Executors.RunnableAdapter 如下:
static final class RunnableAdapter<T> implements Callable<T> { final Runnable task; final T result; RunnableAdapter(Runnable task, T result) { this.task = task; this.result = result; } public T call() { task.run(); return result; } }
可以看到是返回固定的结果, 也就是接受Runnable 的时候,返回的结果是固定的结果
2》然后同上面execute 方法一样,执行execute(ftask); 方法,这时候线程池跑的是FutureTask 对象
3》return ftask; 返回Future 对象,使得可以外部拿该对象可以获取执行结果。
4》线程跑任务会调用java.util.concurrent.FutureTask#run, 方法如下:
public void run() { if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return; try { Callable<V> c = callable; if (c != null && state == NEW) { V result; boolean ran; try { result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } finally { // runner must be non-null until state is settled to // prevent concurrent calls to run() runner = null; // state must be re-read after nulling runner to prevent // leaked interrupts int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } }
2. java.util.concurrent.AbstractExecutorService#submit(java.lang.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; }
根据上面的理解,是返回固定的结果result, 只不过java.util.concurrent.AbstractExecutorService#submit(java.lang.Runnable) 的返回结果是null。
3. java.util.concurrent.AbstractExecutorService#submit(java.util.concurrent.Callable<T>) 理解
(1) 测试代码
Future<String> submit1 = threadPoolExecutor.submit(new Callable<String>() { @Override public String call() throws Exception { log.info("--" + Thread.currentThread().getName() + "--E"); try { Thread.sleep(2 * 1000); } catch (InterruptedException e) { } log.info("--" + Thread.currentThread().getName() + "--F"); return "123456"; } });
(2) 源码:java.util.concurrent.AbstractExecutorService#submit(java.util.concurrent.Callable<T>)
public <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task); execute(ftask); return ftask; }
1》java.util.concurrent.AbstractExecutorService#newTaskFor(java.util.concurrent.Callable<T>) 创建 FutureTask
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { return new FutureTask<T>(callable); }
接着调用:java.util.concurrent.FutureTask#FutureTask(java.util.concurrent.Callable<V>)
public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable }
2》然后调用java.util.concurrent.ThreadPoolExecutor#execute 跑任务,只不过Runnable 对象是java.util.concurrent.FutureTask, 内部的run 方法是调用成员属性Callable 的call 方法,方法执行完之后记录其返回结果, 所以FutureTask 可以拿到返回的结果。
补充:Future 两个重要的方法
(1) java.util.concurrent.FutureTask#isDone 判断是否执行完成
public boolean isDone() { return state != NEW; }
(2) 获取结果:
public V get() throws InterruptedException, ExecutionException { int s = state; if (s <= COMPLETING) s = awaitDone(false, 0L); return report(s); }
java.util.concurrent.FutureTask#awaitDone 阻塞获取结果:
private int awaitDone(boolean timed, long nanos) throws InterruptedException { final long deadline = timed ? System.nanoTime() + nanos : 0L; WaitNode q = null; boolean queued = false; for (;;) { if (Thread.interrupted()) { removeWaiter(q); throw new InterruptedException(); } int s = state; if (s > COMPLETING) { if (q != null) q.thread = null; return s; } else if (s == COMPLETING) // cannot time out yet Thread.yield(); else if (q == null) q = new WaitNode(); else if (!queued) queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q); else if (timed) { nanos = deadline - System.nanoTime(); if (nanos <= 0L) { removeWaiter(q); return state; } LockSupport.parkNanos(this, nanos); } else LockSupport.park(this); } }
可以看到这里使用了 java.util.concurrent.locks.LockSupport#park(java.lang.Object) 进行阻塞线程,下面使用的是UNSAFE 类的相关API。
补充:关于LockSupport 的使用
参考:https://www.jianshu.com/p/8e6b1942ae39
测试代码:
package org.example; import java.util.concurrent.locks.LockSupport; public class PlainTest2 { public static void main(String[] args) { Thread t = new Thread(new Runnable() { @Override public void run() { System.out.println("111222"); LockSupport.park(); System.out.println("333444"); //LockSupport.park(obj); } }); t.start(); try { Thread.sleep(10000); } catch (InterruptedException e) { } System.out.println("555555"); LockSupport.unpark(t); System.out.println("555555"); } }
查看LockSupport 源码:
public static void park() { UNSAFE.park(false, 0L); } public static void unpark(Thread thread) { if (thread != null) UNSAFE.unpark(thread); }
strace linux环境下面监测如下:
1》park
clock_gettime(CLOCK_MONOTONIC, {tv_sec=273, tv_nsec=622979062}) = 0 futex(0xf6797ab8, FUTEX_WAIT_PRIVATE, 1, NULL) = 0 futex(0xf6797a9c, FUTEX_WAIT_PRIVATE, 2, NULL) = 0 futex(0xf6797a9c, FUTEX_WAKE_PRIVATE, 1) = 0 clock_gettime(CLOCK_MONOTONIC, {tv_sec=373, tv_nsec=621209093}) = 0 clock_gettime(CLOCK_MONOTONIC, {tv_sec=373, tv_nsec=621237803}) = 0 futex(0xf6797344, FUTEX_WAIT_PRIVATE, 1, {tv_sec=0, tv_nsec=971290}) = 0 futex(0xf6797328, FUTEX_WAIT_PRIVATE, 2, NULL) = 0 futex(0xf6797328, FUTEX_WAKE_PRIVATE, 1) = 0
2》unpark
write(1, "555555", 6) = 6 write(1, "\n", 1) = 1 clock_gettime(CLOCK_MONOTONIC, {tv_sec=373, tv_nsec=620209800}) = 0 futex(0xf6797ab8, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0xf6797ab4, FUTEX_OP_SET<<28|0<<12|FUTEX_OP_CMP_GT<<24|0x1) = 1 futex(0xf6797a9c, FUTEX_WAKE_PRIVATE, 1) = 1 write(1, "555555", 6) = 6
可以看到到内核调用的也是futex 指令,和synchronized 一样。
3. JDK1.8 的 CompletableFuture
java.util.concurrent.CompletableFuture 实现类实现了接口 Future、 CompletionStage。 Future 接口前面介绍过了,是一个异步的接口,提供了重要的isDone、get() 等方法。 CompletionStage 提供了一个链式操作的API,其重要方法如下:
1. Future 的局限性:
1. Future 的结果只能通过轮询判断isDone 或者 get 阻塞的方式获取结果
2. 多个 Future 不能串联在一起组成链式调用
3. 不能组合多个 Future 的结果
4. 没有异常处理
CompletableFuture 实现了 Future 和 CompletionStage接口,并且提供了许多关于创建,链式调用和组合多个 Future 的便利方法集,而且有广泛的异常处理支持。
CompletableFuture可以从全局的 ForkJoinPool.commonPool()获得一个线程中执行这些任务。但是你也可以创建一个线程池并传给该类的方法来让他们从线程池中获取一个线程执行它们的任务。CompletableFuture API 的所有方法都有两个变体-一个接受Executor作为参数。
可以用join阻塞获取结果,也可以用 get 阻塞获取结果。 区别是 get 会抛出受检查的异常 InterruptedException 和 ExecutionException。
2. 创建CompletableFuture
1. 简单的例子
package org.example; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; public class PlainTest2 { private static final Logger log = LoggerFactory.getLogger(PlainTest.class); public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(5, new ThreadFactory() { private AtomicInteger atomicInteger = new AtomicInteger(0); private String threadNamePrefix = "myExecutor-"; @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); thread.setName(threadNamePrefix + atomicInteger.incrementAndGet()); return thread; } }); // 1. 使用自定义的线程池 CompletableFuture<Void> rFuture = CompletableFuture .runAsync(() -> { log.info("hello 1 start"); try { Thread.sleep(5 * 1000); } catch (InterruptedException e) { } log.info("hello 1 end"); }, executor); // 阻塞等待,runAsync 的future 无返回值,输出null; 且join不会抛出异常 log.info(rFuture.join() + ""); // supplyAsync的使用 CompletableFuture<String> future = CompletableFuture .supplyAsync(() -> { log.info("hello 2 start"); try { Thread.sleep(5 * 1000); } catch (InterruptedException e) { } log.info("hello 2 end"); return "hello 2 result"; }); //阻塞等待 try { log.info(future.get()); } catch (InterruptedException e) { // ignore } catch (ExecutionException e) { // ignore } } }
结果:
2021-08-10 11:16:38 [myExecutor-1] [org.example.PlainTest]-[INFO] hello 1 start 2021-08-10 11:16:43 [myExecutor-1] [org.example.PlainTest]-[INFO] hello 1 end 2021-08-10 11:16:43 [main] [org.example.PlainTest]-[INFO] null 2021-08-10 11:16:43 [ForkJoinPool.commonPool-worker-1] [org.example.PlainTest]-[INFO] hello 2 start 2021-08-10 11:16:48 [ForkJoinPool.commonPool-worker-1] [org.example.PlainTest]-[INFO] hello 2 end 2021-08-10 11:16:48 [main] [org.example.PlainTest]-[INFO] hello 2 result
2. 源码查看:
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) { return asyncSupplyStage(asyncPool, supplier); } public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor) { return asyncSupplyStage(screenExecutor(executor), supplier); } public static CompletableFuture<Void> runAsync(Runnable runnable) { return asyncRunStage(asyncPool, runnable); } public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor) { return asyncRunStage(screenExecutor(executor), runnable); }
(1)java.util.concurrent.CompletableFuture#asyncPool 是默认使用的线程池,默认使用Forkjoinpool.commonPool, 如果禁用就自己创建一个Execotor
private static final Executor asyncPool = useCommonPool ? ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
(2) java.util.concurrent.CompletableFuture#asyncSupplyStage 源码跟踪:
static <U> CompletableFuture<U> asyncSupplyStage(Executor e, Supplier<U> f) { if (f == null) throw new NullPointerException(); CompletableFuture<U> d = new CompletableFuture<U>(); e.execute(new AsyncSupply<U>(d, f)); return d; }
1》java.util.concurrent.CompletableFuture.AsyncSupply:
static final class AsyncSupply<T> extends ForkJoinTask<Void> implements Runnable, AsynchronousCompletionTask { CompletableFuture<T> dep; Supplier<T> fn; AsyncSupply(CompletableFuture<T> dep, Supplier<T> fn) { this.dep = dep; this.fn = fn; } public final Void getRawResult() { return null; } public final void setRawResult(Void v) {} public final boolean exec() { run(); return true; } public void run() { CompletableFuture<T> d; Supplier<T> f; if ((d = dep) != null && (f = fn) != null) { dep = null; fn = null; if (d.result == null) { try { d.completeValue(f.get()); } catch (Throwable ex) { d.completeThrowable(ex); } } d.postComplete(); } } }
继承Forkjointask, ForkJoinTask 又实现Future 接口。
2》可以看到该类的exec 方法调用run() 方法,run() 方法主要就是调用Supplier 的 get() 方法来进行操作,并且返回计算结果。
(3) java.util.concurrent.CompletableFuture#asyncRunStage:
static CompletableFuture<Void> asyncRunStage(Executor e, Runnable f) { if (f == null) throw new NullPointerException(); CompletableFuture<Void> d = new CompletableFuture<Void>(); e.execute(new AsyncRun(d, f)); return d; }
1》 java.util.concurrent.CompletableFuture.AsyncRun
static final class AsyncRun extends ForkJoinTask<Void> implements Runnable, AsynchronousCompletionTask { CompletableFuture<Void> dep; Runnable fn; AsyncRun(CompletableFuture<Void> dep, Runnable fn) { this.dep = dep; this.fn = fn; } public final Void getRawResult() { return null; } public final void setRawResult(Void v) {} public final boolean exec() { run(); return true; } public void run() { CompletableFuture<Void> d; Runnable f; if ((d = dep) != null && (f = fn) != null) { dep = null; fn = null; if (d.result == null) { try { f.run(); d.completeNull(); } catch (Throwable ex) { d.completeThrowable(ex); } } d.postComplete(); } } }
2》 可以看到设计同上面,只是AsyncRun.run 内部调用的是runabble.run, 然后记录结果为null。 结果记录在d 对象,也就是一开始创建的CompletableFuture 对象内部。
2. thenApply、thenAccept、thenRun 可以用于异步处理结果
package org.example; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; public class PlainTest2 { private static final Logger log = LoggerFactory.getLogger(PlainTest.class); public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(5, new ThreadFactory() { private AtomicInteger atomicInteger = new AtomicInteger(0); private String threadNamePrefix = "myExecutor-"; @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); thread.setName(threadNamePrefix + atomicInteger.incrementAndGet()); return thread; } }); // 1. 使用自定义的线程池 CompletableFuture<Void> rFuture = CompletableFuture .runAsync(() -> { log.info("hello 1 start"); try { Thread.sleep(5 * 1000); } catch (InterruptedException e) { } log.info("hello 1 end"); }, executor).thenRunAsync(() -> { // 继续另一个任务 log.info("hello 11 start"); try { Thread.sleep(5 * 1000); } catch (InterruptedException e) { } log.info("hello 11 end"); }, executor); // 阻塞等待,runAsync 的future 无返回值,输出null; 且join不会抛出异常 log.info(rFuture.join() + ""); // thenApply、thenAccept、thenRun 可以用于异步处理结果 CompletableFuture<String> future = CompletableFuture .supplyAsync(() -> { log.info("hello 2 start"); try { Thread.sleep(5 * 1000); } catch (InterruptedException e) { } log.info("hello 2 end"); return "hello 2 result"; }).thenApply(result -> { // 对结果进行转换,可以连续转换 log.info("result: {}", result); return "hello 2 result thenApply"; }); //阻塞等待 try { log.info(future.get()); } catch (InterruptedException e) { // ignore } catch (ExecutionException e) { // ignore } } }
结果:
2021-08-10 12:48:34 [myExecutor-1] [org.example.PlainTest]-[INFO] hello 1 start 2021-08-10 12:48:39 [myExecutor-1] [org.example.PlainTest]-[INFO] hello 1 end 2021-08-10 12:48:39 [myExecutor-2] [org.example.PlainTest]-[INFO] hello 11 start 2021-08-10 12:48:44 [myExecutor-2] [org.example.PlainTest]-[INFO] hello 11 end 2021-08-10 12:48:44 [main] [org.example.PlainTest]-[INFO] null 2021-08-10 12:48:44 [ForkJoinPool.commonPool-worker-1] [org.example.PlainTest]-[INFO] hello 2 start 2021-08-10 12:48:49 [ForkJoinPool.commonPool-worker-1] [org.example.PlainTest]-[INFO] hello 2 end 2021-08-10 12:48:49 [ForkJoinPool.commonPool-worker-1] [org.example.PlainTest]-[INFO] result: hello 2 result 2021-08-10 12:48:49 [main] [org.example.PlainTest]-[INFO] hello 2 result thenApply
3. 合并两个CompletableFuture
测试代码:
package org.example; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiFunction; import java.util.function.Function; public class PlainTest2 { private static final Logger log = LoggerFactory.getLogger(PlainTest.class); public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(5, new ThreadFactory() { private AtomicInteger atomicInteger = new AtomicInteger(0); private String threadNamePrefix = "myExecutor-"; @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); thread.setName(threadNamePrefix + atomicInteger.incrementAndGet()); return thread; } }); CompletableFuture<String> future = CompletableFuture .supplyAsync(() -> { log.info("hello 1 start"); try { Thread.sleep(5 * 1000); } catch (InterruptedException e) { } log.info("hello 1 end"); return "hello 1 result"; }, executor); CompletableFuture<String> future2 = CompletableFuture.completedFuture(" OK"); // 常量任务 // 第一种合并结果 CompletableFuture<String> stringCompletableFuture = future.thenComposeAsync(new Function<String, CompletionStage<String>>() { @Override public CompletionStage<String> apply(String s) { log.info("s: {}", s); return CompletableFuture.completedFuture(s + future2.join()); } }); // 第二种合并: 当两个Future都完成的时候,传给``thenCombine()的回调函数将被调用。 CompletableFuture<Object> objectCompletableFuture = future.thenCombine(future2, new BiFunction<String, String, Object>() { @Override public Object apply(String s, String s2) { return s + s2; } }); //阻塞等待 try { log.info(stringCompletableFuture.get()); log.info(objectCompletableFuture.get() + ""); } catch (InterruptedException e) { // ignore } catch (ExecutionException e) { // ignore } } }
结果:
2021-08-10 13:56:50 [myExecutor-1] [org.example.PlainTest]-[INFO] hello 1 start 2021-08-10 13:56:55 [myExecutor-1] [org.example.PlainTest]-[INFO] hello 1 end 2021-08-10 13:56:55 [ForkJoinPool.commonPool-worker-1] [org.example.PlainTest]-[INFO] s: hello 1 result 2021-08-10 13:56:55 [main] [org.example.PlainTest]-[INFO] hello 1 result OK 2021-08-10 13:56:55 [main] [org.example.PlainTest]-[INFO] hello 1 result OK
4. 组合多个CompletableFuture
allof、anyOf 用于组合多个CompletableFuture,区别是allOf(多个任务都需要执行完, 返回的结果类型是CompletableFuture<Void>, 也就是不接受返回结果)、anyOf(有一个任务执行完即可, 返回值类型是CompletableFuture<Object>)
测试代码:
package org.example; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; public class PlainTest2 { private static final Logger log = LoggerFactory.getLogger(PlainTest.class); public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(5, new ThreadFactory() { private AtomicInteger atomicInteger = new AtomicInteger(0); private String threadNamePrefix = "myExecutor-"; @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); thread.setName(threadNamePrefix + atomicInteger.incrementAndGet()); return thread; } }); CompletableFuture<String> future = CompletableFuture .supplyAsync(() -> { log.info("hello 1 start"); try { Thread.sleep(5 * 1000); } catch (InterruptedException e) { } log.info("hello 1 end"); return "hello 1 result"; }, executor); CompletableFuture<String> future2 = CompletableFuture .supplyAsync(() -> { log.info("hello 2 start"); try { Thread.sleep(10 * 1000); } catch (InterruptedException e) { } log.info("hello 2 end"); return "hello 2 result"; }, executor); // 多个任务的组合: allOf(多个任务都需要执行完, 返回的结果类型是CompletableFuture<Void>, 也就是不接受返回结果)、anyOf(有一个任务执行完即可, 返回值类型是CompletableFuture<Object>) CompletableFuture<Object> voidCompletableFuture = CompletableFuture.anyOf(future, future2); try { // 阻塞等待 log.info(voidCompletableFuture.get() + ""); } catch (InterruptedException e) { // ignore } catch (ExecutionException e) { // ignore } } }
结果:
2021-08-10 14:36:58 [myExecutor-2] [org.example.PlainTest]-[INFO] hello 2 start 2021-08-10 14:36:58 [myExecutor-1] [org.example.PlainTest]-[INFO] hello 1 start 2021-08-10 14:37:03 [myExecutor-1] [org.example.PlainTest]-[INFO] hello 1 end 2021-08-10 14:37:03 [main] [org.example.PlainTest]-[INFO] hello 1 result 2021-08-10 14:37:08 [myExecutor-2] [org.example.PlainTest]-[INFO] hello 2 end
5. 异常处理
方法一:exceptionally-可以在这里记录这个异常并返回一个默认值
package org.example; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; public class PlainTest2 { private static final Logger log = LoggerFactory.getLogger(PlainTest.class); public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(5, new ThreadFactory() { private AtomicInteger atomicInteger = new AtomicInteger(0); private String threadNamePrefix = "myExecutor-"; @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); thread.setName(threadNamePrefix + atomicInteger.incrementAndGet()); return thread; } }); CompletableFuture future = CompletableFuture.supplyAsync(() -> { log.info("======1======"); return "111222"; }, executor).thenApply(result -> { log.info("======2======"); int i = 1 / 0; return "222333"; }).thenApply(result -> { log.info("======3======"); return "333444"; }).exceptionally(exception -> { exception.printStackTrace(); return exception.getMessage(); }); try { // 阻塞等待 log.info(future.get() + ""); } catch (InterruptedException e) { // ignore } catch (ExecutionException e) { // ignore } } }
结果:
2021-08-10 15:04:31 [myExecutor-1] [org.example.PlainTest]-[INFO] ======1====== 2021-08-10 15:04:31 [myExecutor-1] [org.example.PlainTest]-[INFO] ======2====== 2021-08-10 15:04:31 [main] [org.example.PlainTest]-[INFO] java.lang.ArithmeticException: / by zero java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero at java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:273) at java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:280) at java.util.concurrent.CompletableFuture.uniApply(CompletableFuture.java:618) at java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:591) at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:488) at java.util.concurrent.CompletableFuture$AsyncSupply.run$$$capture(CompletableFuture.java:1609) at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Caused by: java.lang.ArithmeticException: / by zero at org.example.PlainTest2.lambda$main$1(PlainTest2.java:31) at java.util.concurrent.CompletableFuture.uniApply(CompletableFuture.java:616) ... 7 more
方法二: handle - 相比exceptionally而言,即可处理上一环节的异常也可以处理其正常返回值, 也就是异常或者不异常都会走handle
测试代码:
package org.example; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; public class PlainTest2 { private static final Logger log = LoggerFactory.getLogger(PlainTest.class); public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(5, new ThreadFactory() { private AtomicInteger atomicInteger = new AtomicInteger(0); private String threadNamePrefix = "myExecutor-"; @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); thread.setName(threadNamePrefix + atomicInteger.incrementAndGet()); return thread; } }); CompletableFuture future = CompletableFuture.supplyAsync(() -> { log.info("======1======"); return "111222"; }, executor).thenApply(result -> { log.info("======2======"); return "222333"; }).thenApply(result -> { log.info("======3======"); return "333444"; }).handle((data, exception) -> { log.info("data: {}", data); if (exception == null) { return "444555"; } exception.printStackTrace(); return exception.getMessage(); }); try { // 阻塞等待 log.info(future.get() + ""); } catch (InterruptedException e) { // ignore } catch (ExecutionException e) { // ignore } } }
结果:
2021-08-10 15:25:17 [myExecutor-1] [org.example.PlainTest]-[INFO] ======1====== 2021-08-10 15:25:17 [myExecutor-1] [org.example.PlainTest]-[INFO] ======2====== 2021-08-10 15:25:17 [myExecutor-1] [org.example.PlainTest]-[INFO] ======3====== 2021-08-10 15:25:17 [myExecutor-1] [org.example.PlainTest]-[INFO] data: 333444 2021-08-10 15:25:21 [main] [org.example.PlainTest]-[INFO] 444555
方法三:whenComplete - 它不参与返回结果的处理,把它当成监听器即可
package org.example; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; public class PlainTest2 { private static final Logger log = LoggerFactory.getLogger(PlainTest.class); public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(5, new ThreadFactory() { private AtomicInteger atomicInteger = new AtomicInteger(0); private String threadNamePrefix = "myExecutor-"; @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); thread.setName(threadNamePrefix + atomicInteger.incrementAndGet()); return thread; } }); CompletableFuture future = CompletableFuture.supplyAsync(() -> { log.info("======1======"); return "111222"; }, executor).thenApply(result -> { log.info("======2======"); int i = 1 / 0; return "222333"; }).thenApply(result -> { log.info("======3======"); return "333444"; }).whenComplete((data, exception) -> { // 它不参与返回结果的处理,把它当成监听器即可 log.info("data: {}", data); if (exception != null) { exception.printStackTrace(); } }); try { // 阻塞等待 log.info(future.get() + ""); } catch (InterruptedException e) { // ignore } catch (ExecutionException e) { // ignore } } }
结果:
2021-08-10 15:49:32 [myExecutor-1] [org.example.PlainTest]-[INFO] ======1====== 2021-08-10 15:49:32 [myExecutor-1] [org.example.PlainTest]-[INFO] ======2====== 2021-08-10 15:49:32 [myExecutor-1] [org.example.PlainTest]-[INFO] data: null java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero at java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:273) at java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:280) at java.util.concurrent.CompletableFuture.uniApply(CompletableFuture.java:618) at java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:591) at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:488) at java.util.concurrent.CompletableFuture$AsyncSupply.run$$$capture(CompletableFuture.java:1609) at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Caused by: java.lang.ArithmeticException: / by zero at org.example.PlainTest2.lambda$main$1(PlainTest2.java:31) at java.util.concurrent.CompletableFuture.uniApply(CompletableFuture.java:616) ... 7 more
补充: 创建一个空的CompletableFuture, 其isDone() 是false,get() 方法会阻塞
// 默认创建一个空的 CompletableFuture , isDone 是false CompletableFuture completableFuture = new CompletableFuture(); System.out.println(completableFuture.isDone()); // 完成该任务,并将结果设为 123 completableFuture.complete(123); System.out.println(completableFuture.isDone()); System.out.println(completableFuture.get());
结果:
false
true
123
java.util.concurrent.CompletableFuture#isDone 方法判断是否完成的依据是根据是否有结果进行判断:
public boolean isDone() { return result != null; }
补充: Callable 实现对数组求和
package com.zd.bx; import java.util.concurrent.*; public class PlainTest { public static void main(String[] args) throws ExecutionException, InterruptedException { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 0, TimeUnit.DAYS, new LinkedBlockingDeque<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); MyRunnable myRunnable = new MyRunnable(new int[]{1, 3}); MyRunnable myRunnable2 = new MyRunnable(new int[]{12, 32}); MyRunnable myRunnable3 = new MyRunnable(new int[]{122, 322}); Future<Integer> submit = threadPoolExecutor.submit(myRunnable); Future<Integer> submit2 = threadPoolExecutor.submit(myRunnable2); Future<Integer> submit3 = threadPoolExecutor.submit(myRunnable3); Integer sum = submit.get() + submit2.get() + submit3.get(); System.out.println(sum); } } class MyRunnable implements Callable<Integer> { private int[] nums; public MyRunnable(int[] nums) { this.nums = nums; } @Override public Integer call() throws Exception { int sum = 0; for (int num : nums) { sum += num; } return sum; } }
补充:关于线程和线程池中的异常
1. 每个线程都有一个uncaughtExceptionHandler, 默认为ThreadGroup 对象, 也可以自己手动设置,用于在线程发生异常之后进行回调。
(1) 其回调是JVM调用java.lang.Thread#dispatchUncaughtException。
/** * Dispatch an uncaught exception to the handler. This method is * intended to be called only by the JVM. */ private void dispatchUncaughtException(Throwable e) { getUncaughtExceptionHandler().uncaughtException(this, e); }
(2) 默认是ThreadGroup
java.lang.Thread#getUncaughtExceptionHandler:
/** * Returns the handler invoked when this thread abruptly terminates * due to an uncaught exception. If this thread has not had an * uncaught exception handler explicitly set then this thread's * <tt>ThreadGroup</tt> object is returned, unless this thread * has terminated, in which case <tt>null</tt> is returned. * @since 1.5 * @return the uncaught exception handler for this thread */ public UncaughtExceptionHandler getUncaughtExceptionHandler() { return uncaughtExceptionHandler != null ? uncaughtExceptionHandler : group; }
查看java.lang.ThreadGroup#uncaughtException:
public void uncaughtException(Thread t, Throwable e) { if (parent != null) { parent.uncaughtException(t, e); } else { Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler(); if (ueh != null) { ueh.uncaughtException(t, e); } else if (!(e instanceof ThreadDeath)) { System.err.print("Exception in thread \"" + t.getName() + "\" "); e.printStackTrace(System.err); } } }
(2) 测试查看调用链如下:
public class PlainTest { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { System.out.println(Thread.currentThread().getId() + "\t111"); int i = 1 / 0; System.out.println(Thread.currentThread().getId() + "\t222"); }); thread.start(); Thread.sleep(3 * 1000); } }
结果:
日志如下:
Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero at PlainTest.lambda$main$0(PlainTest.java:7) at java.lang.Thread.run(Thread.java:748)
(3) 修改异常为自己的处理器
public class PlainTest { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { System.out.println(Thread.currentThread().getId() + "\t111"); int i = 1 / 0; System.out.println(Thread.currentThread().getId() + "\t222"); }); thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println(t.getId() + " error in java.lang.Thread.UncaughtExceptionHandler.uncaughtException"); e.printStackTrace(); } }); thread.start(); Thread.sleep(3 * 1000); } }
日志:
2. 关于线程池中的异常
(1) execute 方法中报异常会导致线程死亡,并且外部无法捕捉到异常。 除非自己对每个任务都try.catch. 或者重写线程的UncaughtExceptionHandler
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class PlainTest { public static void main(String[] args) throws InterruptedException { // final ExecutorService executorService = Executors.newFixedThreadPool(1); ThreadPoolExecutor executorService = new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingQueue(200), new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); thread.setDaemon(false); return thread; } }); for (int i = 0; i < 2; i++) { executorService.execute(() -> { System.out.println(Thread.currentThread().getId() + "\t111"); int index = 1 / 0; System.out.println(Thread.currentThread().getId() + "\t222"); }); } Thread.sleep(3 * 1000); } }
日志如下: (核心池子大小是1, 所以可以看到确实是线程ID发生变化, 也就是发生异常之后线程销毁了, 并且相关日志也是从ThreadGroup 打印出来的。)
13 111 14 111 Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero at PlainTest.lambda$main$0(PlainTest.java:21) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Exception in thread "Thread-1" java.lang.ArithmeticException: / by zero at PlainTest.lambda$main$0(PlainTest.java:21) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)
查看原理:
1》 java.util.concurrent.ThreadPoolExecutor#runWorker

final void runWorker(Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; w.firstTask = null; w.unlock(); // allow interrupts boolean completedAbruptly = true; try { while (task != null || (task = getTask()) != null) { w.lock(); // If pool is stopping, ensure thread is interrupted; // if not, ensure thread is not interrupted. This // requires a recheck in second case to deal with // shutdownNow race while clearing interrupt if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted()) wt.interrupt(); try { beforeExecute(wt, task); Throwable thrown = null; try { task.run(); } catch (RuntimeException x) { thrown = x; throw x; } catch (Error x) { thrown = x; throw x; } catch (Throwable x) { thrown = x; throw new Error(x); } finally { afterExecute(task, thrown); } } finally { task = null; w.completedTasks++; w.unlock(); } } completedAbruptly = false; } finally { processWorkerExit(w, completedAbruptly); } }
task.run(); 相当于是直接调用业务的run 方法,业务方法没有异常处理,所以直接抛异常导致线程结束
(2) submit 会导致异常丢失
import java.util.concurrent.*; public class PlainTest { public static void main(String[] args) throws InterruptedException { // final ExecutorService executorService = Executors.newFixedThreadPool(1); ThreadPoolExecutor executorService = new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingQueue(200), new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); thread.setDaemon(false); return thread; } }); for (int i = 0; i < 2; i++) { executorService.submit(() -> { System.out.println(Thread.currentThread().getId() + "\t111"); int index = 1 / 0; System.out.println(Thread.currentThread().getId() + "\t222"); }); } Thread.sleep(3 * 1000); } }
结果:
13 111 13 111
原理查看:
1》 前面了解到无论是runnable、还是callable 都先包装成callable, 然后包装成FutureTask 对象进行调用。调用submit 的时候相当于包装成一个FutureTask 对象,然后线程调用通过java.util.concurrent.FutureTask#run 方法调用到我们传递的 callable的run方法
public void run() { if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return; try { Callable<V> c = callable; if (c != null && state == NEW) { V result; boolean ran; try { result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } finally { // runner must be non-null until state is settled to // prevent concurrent calls to run() runner = null; // state must be re-read after nulling runner to prevent // leaked interrupts int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } }
可以看到其对callable.run 方法进行了catch, 并且捕捉到异常之后会记录异常状态以及异常信息。调用java.util.concurrent.FutureTask#setException 记录错误结果:
protected void setException(Throwable t) { if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { outcome = t; UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state finishCompletion(); } }
(3) 调用FutureTask.get() 会拿到错误信息:
import java.util.concurrent.*; public class PlainTest { public static void main(String[] args) throws InterruptedException, ExecutionException { // final ExecutorService executorService = Executors.newFixedThreadPool(1); ThreadPoolExecutor executorService = new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingQueue(200), new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); thread.setDaemon(false); return thread; } }); for (int i = 0; i < 1; i++) { Future<?> submit = executorService.submit(() -> { System.out.println(Thread.currentThread().getId() + "\t111"); int index = 1 / 0; System.out.println(Thread.currentThread().getId() + "\t222"); }); submit.get(); } Thread.sleep(3 * 1000); } }
结果:
13 111 Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero at java.util.concurrent.FutureTask.report(FutureTask.java:122) at java.util.concurrent.FutureTask.get(FutureTask.java:192) at PlainTest.main(PlainTest.java:21) Caused by: java.lang.ArithmeticException: / by zero at PlainTest.lambda$main$0(PlainTest.java:18) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266) at java.util.concurrent.FutureTask.run(FutureTask.java) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)
查看原理:
1》 java.util.concurrent.FutureTask#get() 获取结果:
public V get() throws InterruptedException, ExecutionException { int s = state; if (s <= COMPLETING) s = awaitDone(false, 0L); return report(s); }
2》 发生异常会解除阻塞到 java.util.concurrent.FutureTask#report
private V report(int s) throws ExecutionException { Object x = outcome; if (s == NORMAL) return (V)x; if (s >= CANCELLED) throw new CancellationException(); throw new ExecutionException((Throwable)x); }
这里根据状态判断,发生异常之后状态是EXCEPTIONAL。 所以会直接抛出异常 ExecutionException。 所以通过get 可以拿到相关异常信息(对get 方法进行try...catch...可以拿到相关异常)。
补充:关于 applyToEither、allOf、anyOf、增加时间限制等处理
package org.example; import org.apache.commons.lang3.time.DateFormatUtils; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.*; import java.util.function.Function; public class MyTest { public static void main(String[] args) throws ExecutionException, InterruptedException { allOfWithTime(); } private static void allOfWithTime() { /** * 比如说希望多个任务在一定时间内跑完,如果跑不完就丢弃剩下的任务 * 发生异常之后是异常的线程执行异常逻辑 */ // 收集结果 List<Object> results = new ArrayList<>(); CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> { PrintTool.printTimeAndThread("task1 路公交在路上。。。"); PrintTool.sleep(5000); return "task1"; }).handle((result, throwable) -> { String resultStr = ""; if (throwable != null) { resultStr = throwable.getMessage(); } else { resultStr = result; } results.add(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss") + "\t" + resultStr); return resultStr; }); CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> { PrintTool.printTimeAndThread("task2 路公交在路上。。。"); int i = 1 / 0; return "task2"; }).handle((result, throwable) -> { String resultStr = ""; if (throwable != null) { resultStr = throwable.getMessage(); } else { resultStr = result; } results.add(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss") + "\t" + resultStr + "\t" + Thread.currentThread().getName()); return resultStr; }); CompletableFuture<String> task3 = CompletableFuture.supplyAsync(() -> { PrintTool.printTimeAndThread("task3 路公交在路上。。。"); return "task3"; }).handle((result, throwable) -> { String resultStr = ""; if (throwable != null) { resultStr = throwable.getMessage(); } else { resultStr = result; } results.add(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss") + "\t" + resultStr); return resultStr; }); // 合并 CompletableFuture<Void> voidCompletableFuture = CompletableFuture.allOf(task1, task2, task3); // 增加时间限制 long times = 2000; CompletableFuture<Void> objectCompletableFuture = new CompletableFuture<>(); // 定时任务跑,超过在到达时间之后将objectCompletableFuture 按异常完成 ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); scheduler.schedule(() -> { PrintTool.printTimeAndThread("执行超时~~~"); objectCompletableFuture.completeExceptionally(new RuntimeException("执行超时, ThreadName: " + Thread.currentThread().getName())); }, times, TimeUnit.MILLISECONDS); // 用applyToEither 和上面取结束快的。以此达到限时的效果。 voidCompletableFuture.applyToEither(objectCompletableFuture, Function.identity()) .exceptionally((throwable -> { PrintTool.printTimeAndThread("执行超时异常拦截到~~~"); throwable.printStackTrace(); return null; })).join(); System.out.println(results); /** * 1. 继续完善,想看有哪些任务超时了,可以在任务开始的时候添加到一个缓存里、任务跑完从缓存移除。 * 2. 抛出异常的时候看还剩哪些任务 */ /** * 2023-01-04 12:54:19 name: task3 路公交在路上。。。 ForkJoinPool.commonPool-worker-3 * 2023-01-04 12:54:19 name: task1 路公交在路上。。。 ForkJoinPool.commonPool-worker-1 * 2023-01-04 12:54:19 name: task2 路公交在路上。。。 ForkJoinPool.commonPool-worker-2 * 2023-01-04 12:54:21 name: 执行超时~~~ pool-1-thread-1 * 2023-01-04 12:54:21 name: 执行超时异常拦截到~~~ pool-1-thread-1 * [2023-01-04 12:54:19 task3, 2023-01-04 12:54:19 java.lang.ArithmeticException: / by zero ForkJoinPool.commonPool-worker-2] * java.util.concurrent.CompletionException: java.lang.RuntimeException: 执行超时, ThreadName: pool-1-thread-1 * at java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:292) * at java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:308) * at java.util.concurrent.CompletableFuture.orApply(CompletableFuture.java:1385) * at java.util.concurrent.CompletableFuture$OrApply.tryFire(CompletableFuture.java:1364) * at java.util.concurrent.CompletableFuture$CoCompletion.tryFire(CompletableFuture.java:1034) * at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:488) * at java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:1990) * at org.example.MyTest.lambda$allOfWithTime$6(MyTest.java:75) * at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) * at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266) * at java.util.concurrent.FutureTask.run(FutureTask.java) * at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) * at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) * at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) * at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) * at java.lang.Thread.run(Thread.java:750) * Caused by: java.lang.RuntimeException: 执行超时, ThreadName: pool-1-thread-1 * ... 9 more */ } public static void anyOf() { /** * 举个例子: * 张三下班后坐公交回家,可以选择 539 路和 906 路,当其中有一辆公交到达,张三就选择坐这辆车回家。但是张三坐车走之后,另一辆公交车还会继续运行 * 也就是一个线程完成之后,将结果返回去但是在途的线程仍然在跑。 */ PrintTool.printTimeAndThread("张三下班准备回家。。。"); PrintTool.printTimeAndThread("张三等待906,539公交。。。"); CompletableFuture<String> car906 = CompletableFuture.supplyAsync(() -> { PrintTool.printTimeAndThread("906 路公交在路上。。。"); PrintTool.sleep(5000); PrintTool.printTimeAndThread("906 公交过来了"); return "906"; }); CompletableFuture<String> car539 = CompletableFuture.supplyAsync(() -> { PrintTool.printTimeAndThread("539 路公交在路上。。。"); PrintTool.sleep(4000); PrintTool.printTimeAndThread("539 公交过来了"); return "539"; }); // anyOf 用法. 任何一个现场执行完就结束 CompletableFuture<Object> objectCompletableFuture = CompletableFuture.anyOf(car906, car539); PrintTool.printTimeAndThread("张三坐上" + objectCompletableFuture.join()); try { Thread.sleep(2000); } catch (InterruptedException e) { throw new RuntimeException(e); } /** * 2023-01-03 20:37:36 name: 张三下班准备回家。。。 main * 2023-01-03 20:37:36 name: 张三等待906,539公交。。。 main * 2023-01-03 20:37:36 name: 906 路公交在路上。。。 ForkJoinPool.commonPool-worker-1 * 2023-01-03 20:37:36 name: 539 路公交在路上。。。 ForkJoinPool.commonPool-worker-2 * 2023-01-03 20:37:40 name: 539 公交过来了 ForkJoinPool.commonPool-worker-2 * 2023-01-03 20:37:40 name: 张三坐上539 main * 2023-01-03 20:37:41 name: 906 公交过来了 ForkJoinPool.commonPool-worker-1 */ } public static void allOf() throws ExecutionException, InterruptedException { /** * 张三下班后坐公交回家,可以选择 539 路和 906 路,两个车都到达才回家 */ PrintTool.printTimeAndThread("张三下班准备回家。。。"); PrintTool.printTimeAndThread("张三等待906,539公交。。。"); CompletableFuture<String> car906 = CompletableFuture.supplyAsync(() -> { PrintTool.printTimeAndThread("906 路公交在路上。。。"); PrintTool.sleep(5000); return "906"; }); CompletableFuture<String> car539 = CompletableFuture.supplyAsync(() -> { PrintTool.printTimeAndThread("539 路公交在路上。。。"); PrintTool.sleep(4000); return "539"; }); List<CompletableFuture<String>> objects = new ArrayList<>(); objects.add(car906); objects.add(car539); // all of 等待所有执行完 CompletableFuture<Void> voidCompletableFuture = CompletableFuture.allOf(car906, car539); // 合并后的现场结束,那么就可以从上面的现场获取结果 PrintTool.printTimeAndThread("等待公交。。。"); Void finished = voidCompletableFuture.get(); for (CompletableFuture<String> object : objects) { PrintTool.printTimeAndThread("张三坐上" + object.get()); } /** * 2022-12-27 18:02:11 name: 张三下班准备回家。。。 main * 2022-12-27 18:02:11 name: 张三等待906,539公交。。。 main * 2022-12-27 18:02:11 name: 906 路公交在路上。。。 ForkJoinPool.commonPool-worker-1 * 2022-12-27 18:02:11 name: 539 路公交在路上。。。 ForkJoinPool.commonPool-worker-2 * 2022-12-27 18:02:11 name: 等待公交。。。 main * 2022-12-27 18:02:16 name: 张三坐上906 main * 2022-12-27 18:02:16 name: 张三坐上539 main */ } public static void applyToEither() throws ExecutionException, InterruptedException { /** * 举个例子: * 张三下班后坐公交回家,可以选择 539 路和 906 路,当其中有一辆公交到达,张三就选择坐这辆车回家。但是等待过程中520 来了就乘坐520 * * 虽然拿到了结果,但是其他公交还在运行。也就是其他线程还在跑,不会中断。 */ PrintTool.printTimeAndThread("张三下班准备回家。。。"); PrintTool.printTimeAndThread("张三等待906,539公交。。。"); CompletableFuture<String> car906 = CompletableFuture.supplyAsync(() -> { PrintTool.printTimeAndThread("906 路公交在路上。。。"); PrintTool.sleep(5000); PrintTool.printTimeAndThread("906 路公交到了。。。"); return "906"; }); CompletableFuture<String> car539 = CompletableFuture.supplyAsync(() -> { PrintTool.printTimeAndThread("539 路公交在路上。。。"); PrintTool.sleep(4000); PrintTool.printTimeAndThread("539 路公交到了。。。"); return "539"; }); // any of合并 CompletableFuture<Object> objectCompletableFuture = CompletableFuture.anyOf(car906, car539); // applyToEither 方法主要作用:两个 CompletionStage,谁计算的快,我就用那个 CompletionStage 的结果进行下一步的转化操作。第二个参数代表对最快返回的那个结果进行从处理。 // acceptEither 跟 applyToEither 类似,但是返回 CompletableFuture<Void> 类型。 CompletableFuture<Object> stringCompletableFuture = CompletableFuture.supplyAsync(() -> { PrintTool.printTimeAndThread("520 路公交在路上。。。"); PrintTool.sleep(2000); PrintTool.printTimeAndThread("520 路公交到了。。。"); return "520"; }); /*CompletableFuture<Object> objectCompletableFuture1 = objectCompletableFuture.applyToEither(stringCompletableFuture, new Function<Object, Object>() { @Override public Object apply(Object o) { PrintTool.printTimeAndThread("o is " + o); return o; } });*/ CompletableFuture<Object> objectCompletableFuture1 = objectCompletableFuture.applyToEither(stringCompletableFuture, Function.identity()); PrintTool.printTimeAndThread(objectCompletableFuture1.get().toString()); Thread.sleep(5 * 1000); /** * stringCompletableFuture 睡眠2000 结果 * * 2023-01-03 20:43:29 name: 张三下班准备回家。。。 main * 2023-01-03 20:43:29 name: 张三等待906,539公交。。。 main * 2023-01-03 20:43:29 name: 539 路公交在路上。。。 ForkJoinPool.commonPool-worker-2 * 2023-01-03 20:43:29 name: 906 路公交在路上。。。 ForkJoinPool.commonPool-worker-1 * 2023-01-03 20:43:29 name: 520 路公交在路上。。。 ForkJoinPool.commonPool-worker-3 * 2023-01-03 20:43:31 name: 520 路公交到了。。。 ForkJoinPool.commonPool-worker-3 * 2023-01-03 20:43:31 name: 520 main * 2023-01-03 20:43:33 name: 539 路公交到了。。。 ForkJoinPool.commonPool-worker-2 * 2023-01-03 20:43:34 name: 906 路公交到了。。。 ForkJoinPool.commonPool-worker-1 */ /** * stringCompletableFuture 睡眠6000 结果 * * 2023-01-03 20:41:19 name: 张三下班准备回家。。。 main * 2023-01-03 20:41:20 name: 张三等待906,539公交。。。 main * 2023-01-03 20:41:20 name: 906 路公交在路上。。。 ForkJoinPool.commonPool-worker-1 * 2023-01-03 20:41:20 name: 539 路公交在路上。。。 ForkJoinPool.commonPool-worker-2 * 2023-01-03 20:41:20 name: 520 路公交在路上。。。 ForkJoinPool.commonPool-worker-3 * 2023-01-03 20:41:24 name: 539 路公交到了。。。 ForkJoinPool.commonPool-worker-2 * 2023-01-03 20:41:24 name: 539 main * 2023-01-03 20:41:25 name: 906 路公交到了。。。 ForkJoinPool.commonPool-worker-1 * 2023-01-03 20:41:26 name: 520 路公交到了。。。 ForkJoinPool.commonPool-worker-3 **/ } public static class PrintTool { public static void printTimeAndThread(String name) { System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss") + "\tname: " + name + "\t" + Thread.currentThread().getName()); } public static void sleep(int i) { try { Thread.sleep(i); } catch (InterruptedException e) { throw new RuntimeException(e); } } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2020-08-09 Java操作ElasticSearch
2020-08-09 ES索引Index相关操作&ES数据类型、字符串类型text和keyword区别
2018-08-09 Struts2不扫描jar包中的action