线程池带来的好处:
降低资源效率---重复利用已创建的线程降低线程创建和销毁造成的消耗。
提高响应速度---当任务到达时,不用等待线程的创建立即运行。
提高线程的可管理性---可对线程池中进行统一的分配、调优、监控。
- 判断核心线程池的线程是否都在执行任务,不是,则创建一个新的工作线程来执行任务,如果都在,则进入下一流程。
- 线程池中的工作队列是否已满,没满,则将任务提交到这个工作队列中,满了进入下一流程。
- 判断线程池的线程是否都处于工作状态,不是,创建一个新的工作线程执行任务,是则根据饱和策略来处理这个线程。
- 当前运行的线程小于corePoolSize,则创建新的线程的执行任务(获取全局锁)。
- 如果允许线程等与或多余corePoolSize,则加入blockingQueue任务队列。
- 如果无法将任务加入blockingQueue,则创建新的线程来处理任务(获取全局锁)。
- 当创建线程将使得运行的线程数超过maxPoolSize,任务将拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法,有包和策略处理。
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get(); //当前工作线程是否小于corePoolSize 创建新的工作线程。 if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; c = ctl.get(); } //运行线程数大于等于corePoolSize 进入任务队列 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); } //任务队列已满,能否添加新的工作线程,不能reject else if (!addWorker(command, false)) reject(command); } private boolean addWorker(Runnable firstTask, boolean core) { retry: for (;;) { int c = ctl.get(); int rs = runStateOf(c); 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; } } boolean workerStarted = false; boolean workerAdded = false; ThreadPoolExecutor.Worker w = null; try { //创建新的工作线程 w = new Worker(firstTask); final Thread t = w.thread; if (t != null) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { 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; } public void run() { runWorker(this); } final void runWorker(ThreadPoolExecutor.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 ((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); } } 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 { //take或poll获取工作线程 poll 超时拿去,如果keepAliveTime时间到了拿不到, //就返回null,也就把这个工作线程干掉 Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); if (r != null) return r; timedOut = true; } catch (InterruptedException retry) { timedOut = false; } } } private void processWorkerExit(Worker w, boolean completedAbruptly) { if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted decrementWorkerCount(); final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { completedTaskCount += w.completedTasks; //移除 workers.remove(w); } finally { mainLock.unlock(); } tryTerminate(); int c = ctl.get(); if (runStateLessThan(c, STOP)) { if (!completedAbruptly) { int min = allowCoreThreadTimeOut ? 0 : corePoolSize; if (min == 0 && ! workQueue.isEmpty()) min = 1; if (workerCountOf(c) >= min) return; // replacement not needed } addWorker(null, false); } }
- execute执行后,创建新的工作线程,执行当前任务。
- 执行完后,到BlockingQueu里去反复获取任务来执行。
线程池创建参数:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
- corePoolSize(线程池的基本大小):当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任 务数大于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads()prestartCoreThreads或方法, 线程池会提前创建并启动一个或所有基本线程。
maximumPoolSize ( 线 程池最大数量): 线 程池允 许创 建的最大 线 程数。如果 队列满了,并且已创建的线程数小于最大线 程数, 则线程池会再创建新的线程执行任务 。 值 得注意的是,如果使用了无界的任务队 列这 个参数就没什么效果。
- keepAliveTime(线程活动保持时间):多余的空闲线程等待新任务的最长时间,超过这个时间后多余的线程将被终止,所以,如果任务很多,并且每个任务执行的时间比较短,可以调大时间,提高线程的利用率。
- TimeUnit(线程活动保持时间的单位):可选的单位有天(DAYS)、小时(HOURS)、分钟(MINUTES)、毫秒(MILLISECONDS)、微秒(MICROSECONDS,千分之一毫秒)和纳秒(NANOSECONDS,千分之一微秒)。
- runnableTaskQueue(任务队列):用于保存等待执行的任务的阻塞队列。ArrayBlockingQueue、LinkedBlockingQueue(吞吐量高与ArrayBlocking)、SynchronousQueue(吞吐量高于LinkedBlocking)、PriorityBlockingQueue
- ThreadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。使用开源框架guava提供的ThreadFactoryBuilder可以快速给线程池里的线 程设置有意义的名字,
- RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法 处理新任务时抛出异常。CallerRunsPolicy:只用调用者所在线程来运行任务。DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。 DiscardPolicy:不处理,丢弃掉。
提交任务
execute提交无返回值的任务。submit()提交有返回值的任务,返回future判断任务是否执行成功。future.get会在阻塞直到任务完成。
线程关闭
shutdown 或 shutdownNow 方法来关 闭线 程池。它 们 的原理是遍 历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终 止。但是它 们 存在一定的区 别 , shutdownNow 首先将 线 程池的状 态设 置成 STOP,然后 尝试停止所有的正在执行或暂停任务的线 程,并返回等待 执行任务 的列表,而 shutdown只是将 线 程池的状 态设 置成 SHUTDOWN 状 态 ,然后中断所有没有正在 执行任务的 线程。当所有任务关闭后才表示线程池关闭成功。shutdown会等待任务执行完毕,shutdownNow会中断工作线程并暂停任务,返回等待的任务列表。
配置线程池
- 任务的性质:CPU密集型任务、IO密集型任务和混合型任务。
- 任务的优先级:高、中和低。
- 任务的执行时间:长、中和短。
- 任务的依赖性:是否依赖其他系统资源,如数据库。
CPU 密集型任 务应 配置尽可能小的线程,如配置 N cpu +1 个 线 程的 线 程池。IO 密集型任 务线 程并不是一直在 执 行任 务 , 则应 配 置尽可能多的线 程,如 2*N cpu 。混合型的任务 ,如果可以拆分,将其拆分成一个 CPU 密集型任 务和一个IO 密集型任 务 ,只要 这 两个任 务执 行的 时间 相差不是太大,那么分解后 执 行的吞吐量 将高于串行执 行的吞吐量。
Executor框架jvm中,java线程被一对一映射为本地操作系统线程,启动后会创建一个本地操作系统线程,终止时,这个本地操作系统线程会被回收,操作系统会调度所有线程并将它们分配给可用的CPU。Java 多 线 程程序通常把 应 用分解 为 若干个任 务 ,然后使用用 户级的调度器(Executor框架)将这些任务映射为固定数量的线 程;在底 层 ,操作系 统 内核将这些线程映射到硬件处 理器上。应用程序通过Executor框架控制上层的调度,而下层调度由操作系统内核控制,下层调度不受上层影响。![]()
Executor由三部分组成:
- 任务:被执行任务需要实现的Runnable接口、Callable接口
- 任务的执行:包括任务执行机制的核心接口Executor,以及继承自Executor的ExecutorService接口。Executor框架有两个关键类实现了ExecutorService接口(ThreadPoolExecutor和ScheduledThreadPoolExecutor)。
- 异步计算的结果:Future接口和实现Future的FutrueTask类。
将任务的提交和任务的执行分离。
![]()
![]()
ThreadPoolExecutorThreadPoolExecutor 通常使用工厂 类 Executors 来 创 建。 Executors 可以 创 建 3 种 类 型的 SingleThreadExecutor 、 FixedThreadPool 和 CachedThreadPool 。
- FixedThreadPool----创建使用固定线程数,使用LinkedBlockingQueue无界任务队列。
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }- SingleThreadExecutor---创建单个线程,使用LinkedBlockingQueue无界任务队列。
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }- CachedThreadPool--- 根据需要创建线程 ,最多Integer.MAX_VALUE,SynchronousQueue任务队列----适用于短期任务。
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); } ForkJoinPool--窃取线程池,并行数等于cpu核数或自定义指定。 new ForkJoinPool (Runtime.getRuntime().availableProcessors(), ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true);ScheduledThreadPoolExecutor 定时或延迟执行任务
- ·ScheduledThreadPoolExecutor---可指定coreSIze,使用DelayedWorkQueue()无界。
- SingleThreadScheduledExecutor--单线程,使用 LinkedBlockingQueue()。
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { return e.schedule(command, delay, unit); } public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { return e.schedule(callable, delay, unit); } public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { return e.scheduleAtFixedRate(command, initialDelay, period, unit); } public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { return e.scheduleWithFixedDelay(command, initialDelay, delay, unit); } period:执行周期 initialDelay:执行时间 delay:延迟执行时间
public RunnableScheduledFuture<?> take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { for (;;) { RunnableScheduledFuture<?> first = queue[0]; if (first == null) available.await(); else { //判断时间 long delay = first.getDelay(NANOSECONDS); if (delay <= 0) //取出任务 return finishPoll(first); first = null; // don't retain ref while waiting if (leader != null) available.await(); else { Thread thisThread = Thread.currentThread(); leader = thisThread; try { available.awaitNanos(delay); } finally { if (leader == thisThread) leader = null; } } } } } finally { if (leader == null && queue[0] != null) available.signal(); lock.unlock(); } }
Future 接口和 实现 Future 接口的 FutureTask 类 用来表示异步 计 算的 结果。Runnable不会返回结果,而Callable可以返回结果。当一个线程需要等待另一个线程把某个任务执行完后它才能继续执 行,此 时 可以使用 FutureTask。FutureTask也实现Runnable接口,可以交给给Executor去执行或调用自己的run方法。使用CAS实现![]()
private final ConcurrentMap<Object, Future<String>> taskCache = new ConcurrentHashMap<Object, Future<String>>(); private String executionTask(final String taskName) throws ExecutionException, InterruptedException { while (true) { Future<String> future = taskCache.get(taskName); if (future == null) { Callable<String> task = new Callable<String>() { public String call() throws InterruptedException { return taskName; } }; FutureTask<String> futureTask = new FutureTask<String>(task); future = taskCache.putIfAbsent(taskName, futureTask); if (future == null) { future = futureTask; futureTask.run(); } } try { return future.get(); } catch (CancellationException e) { taskCache.remove(taskName, future); } } }