线程池可以通过重用已存在的线程,降低线程创建和销毁造成的消耗。并且方便线程并发数的管控。因为线程若是无限制的创建,可能会导致内存占用过多而产生OOM,并且会造成CPU过度切换(CPU切换线程是有时间成本的,需要保持当前执行线程的现场,并恢复要执行线程的现场)
核心参数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
创建线程池的核心参数有以下几种
-
corePoolSize:核心线程池的大小。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建新的线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程;
-
maximumPoolSize:线程池最大线程数,它表示在线程池中最多能创建多少个线程,通常大于corePoolSize;
-
keepAliveTime:表示一个线程在没有任务执行时,最多保持多久会被终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
-
unit:参数keepAliveTime的时间单位;
-
workQueue:一个阻塞队列,用来存储等待执行的任务;
-
ArrayBlockingQueue:一个基于数组的有界(不能存储无限多元素,不扩容)阻塞队列。其存取数据用的都是同一个锁,无法真正做到并行运行。在创建ArrayBlockingQueue时,我们还可以控制对象的内部锁是否采用公平锁,默认采用非公平锁。
-
LinkedBlockingQueue:一个基于链表的有界(不能存储无限多元素,不扩容)阻塞队列。其对于存取数据分别采用了独立的锁来控制数据同步,以此来提高整个队列的并发性能。但,LinkedBlockingQueue相较于ArrayBlockingQueue,在数据结构上多了一个Node对象,所以在插入删除时需要多一个创建和销毁的操作,对于效率存在一定影响。另外,构造LinkedBlockingQueue时,如果不指定容量大小,会默认一个类似无限大小的容量(Integer.MAX_VALUE),可能会导致内存溢出。
-
DelayQueue:一个无界阻塞队列。当其指定的延迟时间到了,才能够从队列中获取到该元素。往队列中插入数据的操作永远不会被阻塞,而只有获取数据的操作才会被阻塞。
-
PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。往队列中插入数据的操作永远不会被阻塞,而只有获取数据的操作才会被阻塞。所以需要注意内存溢出的情况。
-
SynchronousQueue:一个不存储元素的阻塞队列。其特点在于没有容器。一个线程插入数据必须阻塞等待另一个线程取出数据。
-
-
threadFactory:线程工厂,主要用来创建线程。
-
handler:表示当拒绝处理任务时的策略。
-
AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
-
DiscardPolicy:也是丢弃任务,但是不抛出异常。
-
DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
-
CallerRunsPolicy:由调用线程处理该任务
-
四种线程池
-
newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
-
newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小java一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
-
newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
-
newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); }