- 参考 https://www.cnblogs.com/kuoAT/p/6714762.html
-
线程池一般就是这么用的
public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(8); Future<Integer> future = executorService.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { // doSomething(); return null; } }); try { System.out.println(future.get()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } }
也就是说 submit 一个任务,然后用 future 对象来异步获取结果
-
几个类和接口的继承关系是
Executor <— ExecutorService <— AbstractExecutorService <— ThreadPoolExecutor
而创建线程池的时候使用的其实就是 ThreadPoolExecutor
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
-
进入 submit 方法是这样的
public <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task); execute(ftask); return ftask; }
可以看到 submit 方法其实是给 execute 方法套了一个壳子,然后返回一个 future 对象,所以重点还是要看 execute 方法
-
在了解将任务提交给线程池到任务执行完毕整个过程之前,我们先来看一下 ThreadPoolExecutor 类中其他的一些比较重要成员变量
public class ThreadPoolExecutor extends AbstractExecutorService { ... private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); // 用来代表有效线程数量和线程池状态 private final BlockingQueue<Runnable> workQueue; // 任务缓存队列,用来存放等待执行的任务 private final ReentrantLock mainLock = new ReentrantLock(); // 线程池的主要状态锁,对线程池状态(比如线程池大小、runState等)的改变都要使用这个锁 private final HashSet<Worker> workers = new HashSet<Worker>(); // 用来存放工作集 private final Condition termination = mainLock.newCondition(); // 终止线程池时的条件变量 private volatile long keepAliveTime; // 线程存活时间 private volatile boolean allowCoreThreadTimeOut; // 是否允许为核心线程设置存活时间 private volatile int corePoolSize; //核心池的大小(即线程池中的线程数目大于这个参数时,提交的任务会被放进任务缓存队列) private volatile int maximumPoolSize; // 线程池最大能容忍的线程数 private volatile int poolSize; // 线程池中当前的线程数 private volatile RejectedExecutionHandler handler; // 任务拒绝策略 private volatile ThreadFactory threadFactory; // 线程工厂,用来创建线程 private int largestPoolSize; // 用来记录线程池中曾经出现过的最大线程数 private long completedTaskCount; // 用来记录已经执行完毕的任务个数 ... }
-
接下来看 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); }
这里面 addWorker 会开新线程
-
addWorker 方法
private boolean addWorker(Runnable firstTask, boolean core) { retry: for (int c = ctl.get();;) { // Check if queue empty only if necessary. if (runStateAtLeast(c, SHUTDOWN) && (runStateAtLeast(c, STOP) || firstTask != null || workQueue.isEmpty())) return false; for (;;) { if (workerCountOf(c) >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK)) return false; if (compareAndIncrementWorkerCount(c)) break retry; c = ctl.get(); // Re-read ctl if (runStateAtLeast(c, SHUTDOWN)) 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 c = ctl.get(); if (isRunning(c) || (runStateLessThan(c, STOP) && 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; }
这个方法前半部分是各种 CAS 操作,后半部分是在加锁的前提下,尝试添加worker,让worker的新线程start
-
reject 方法用到了策略模式,将四种基本的拒绝策略封装好交给各自实现
final void reject(Runnable command) { handler.rejectedExecution(command, this); }