1、背景java
线程属于系统稀缺资源,在使用线程时,若是无限制建立线程,达到CPU高负荷时,会致使系统运行缓慢,更有甚者直接宕机。数组
在这样的基础上咱们但愿在使用线程时,竟可能使系统线程数处于一个可控范围,尽量实现线程的重用。并发
2、Executors 分析app
Executors 示例 DEMOide
/** * @author binH * @date 2018/01/24 */ package org.lsnbin.thread; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadAnalyze { private static ExecutorService fexecutor = Executors.newFixedThreadPool(20); public static void main(String[] args) { for (int i = 0; i < 20; i++) { fexecutor.execute(new ThreadAnalyze().new Task()); } } private class Task implements Runnable{ @Override public void run() { System.out.println("Thread name --> " + Thread.currentThread().getName()); } } }
示例分析:高并发
一、使用Executors初始化一个包含10个线程的线程池性能
二、使用execute方法提交20个任务,打印线程名this
三、负责执行任务的线程的生命周期交由Executors管理。spa
3、Executors 内部分析线程
Executors是线程池的工厂类,内部基于ThreadPoolExecutor实现,对ThreadPoolExecutor进行封装,并提供相对应方法。
newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
初始化必定数量的线程池,当corePoolSize=maximumPoolSize时,使用LinkedBlockingQueue做为阻塞队列,不过当线程池不使用时,也不会释放线程。
newCachedThreadPool
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
不初始化线程,在须要使用时建立线程,最大容许数为Integer.MAX_VALUE,使用SynchronousQueue做为阻塞队列,当线程空闲时keepAliveTime后释放线程。
容易存在问题:在高并发状况下,一瞬间会建立大量的线程,会出现严重的性能问题。
newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
初始化只有一个线程的线程池,若是该线程因为异常结束,会从新建立一个新的线程。线程不过时,使用LinkedBlockingQueue做为阻塞队列。惟一个线程能够保证任务的顺序执行。
newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); }
初始化必定数量线程的线程池,线程池中的任务能够在指定的时间内周期性的执行所提交的任务。可用于按期同步。
newWorkStealingPool -- JDK1.8
public static ExecutorService newWorkStealingPool(int parallelism) { return new ForkJoinPool (parallelism, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true); }
建立持有足够线程的线程池来支持给定的并行级别,并经过使用多个队列来减小竞争,须要指定parallelism并行参数,若是没指定,则默认CPU数量。
ForkJoinPool:支持大任务分解成小任务的线程池,这是Java8新增线程池,一般配合ForkJoinTask接口的子类RecursiveAction或RecursiveTask使用。
4、ThreadPoolExecutor 分析
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.acc = System.getSecurityManager() == null ? null : AccessController.getContext(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
参数分析:
一、corePoolSize
线程池核心线程数。当线程池提交一个任务时,线程池就会建立一个新线程并执行任务,直到线程池内运行状态的线程数等于corePoolSize。
当继续有任务提交时,会被放入阻塞队列中。使用 prestartCoreThread() 方法能够提早预建立并启动全部核心线程。
二、maximumPoolSize
线程池最大可用线程数。在线程数等于corePoolSize时,若是当前阻塞队列满了,继续提交任务时会建立新的线程并执行任务。前提时线程数小于maximumPoolSize。
三、keepAliveTime
线程池中线程空闲存活时间。默认状况下该状况只会在大于corePoolSize时有用。可是只要keepAliveTime值非0,能够经过allowallowCoreThreadTimeOut(boolean value) 方法将超时策略应用于核心线程。
四、unit
keepAliveTime存活时间的单位。
五、workQueue
用来保存等待被执行的任务的阻塞队列。JDK实现的队列有如下几种:
ArrayBlockingQueue:基于数组结构的有界阻塞队列,按照FIFO ( 先进先出 ) 排序任务。
LinkedBlockingQueue:基于链表结构的阻塞队列,按照FIFO ( 先进先出 ) 排序任务。效果比ArrayBlockingQueue好。
DelayQueue:具备延时执行的无界阻塞队列。
SynchronousQueue:一个不能存储任何元素的阻塞队列,每一个插入的操做必须等待另外一个相应的删除操做,不然插入则会一直处于阻塞状态。
第二个线程必须等待前一个线程结束。
PriorityBlockingQueue:具备 优先级的无界阻塞队列。
六、threadFactory
建立线程的工厂
默认工厂实现:
DefaultThreadFactory() { SecurityManager s = System.getSecurityManager(); group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-"; }
七、handler
线程池的饱和策略,用于线程数饱和与阻塞队列已满的状况。有任务继续提交时触发的处理逻辑。线程池提供了4种策略:
AbortPolicy:直接跑出异常,默认策略。
DiscardPolicy:直接丢弃任务,不处理。
DiscardOldestPolicy:丢弃阻塞队列中靠前的旧的任务,执行新的任务。
CallerRunsPolicy:用调用者的线程来执行任务。