赞
踩
线程池,顾名思义就是若干个线程的集合。在程序中通过使用线程池,减少cpu和内存的使用。节省线程的创建和销毁时间,下面将对线程池进行介绍。
举个例子,创建一个核心线程数为10,最大线程数为15的线程池,当一个任务提交,首先会判断当前线程数是否达到核心线程数也就是10,如果达没有达到,将会创建线程来执行任务,如果达到核心线程数,将会判断队列已满,如果未满,则会将任务添加到队列等待执行,如果满了,判断线程池是否已满线程数达到15,如果没满,创建非核心线程来执行任务。如果线程池已满,将会执行拒绝策略。
1、拒绝策略
(1) AbortPolicy:中断抛出异常
(2)DiscardPolicy:默默丢弃任务,不进行任何通知
(3)DiscardOldestPolicy:丢弃掉在队列中存在时间最久的任务
(4) CallerRunsPolicy:让提交任务的线程去执行任务(对比前三种比较友好一丢丢)
2、队列
(1)SynchronousQueue(同步移交队列):队列不作为任务的缓冲方式,可以简单理解为队列长度为零
(2)LinkedBlockingQueue(无界队列):队列长度不受限制,当请求越来越多时(任务处理速度跟不上任务提交速度造成请求堆积)可能导致内存占用过多或OOM
3、源码分析
添加任务并启动执行
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; }
在java中有如下四种的线程池创建方式:
1、创建一个固定大小的线程池
public class FixedThreadPoolTest { public static void main(String[] args) { //1.创建一个大小为5的线程池 ExecutorService threadPool= Executors.newFixedThreadPool(5); //2.使用线程池执行任务一 for (int i=0;i<5;i++){ //给线程池添加任务 threadPool.submit(() -> System.out.println("线程名"+Thread.currentThread().getName()+"在执行任务1")); } //3.使用线程池执行任务二 for (int i=0;i<10;i++){ //给线程池添加任务 threadPool.submit(() -> System.out.println("线程名"+Thread.currentThread().getName()+"在执行任务2")); } //4.使用线程池执行任务二 for (int i=0;i<20;i++){ //给线程池添加任务 threadPool.submit(() -> System.out.println("线程名"+Thread.currentThread().getName()+"在执行任务2")); } } }
2、创建大小不限制的线程池,基于缓存,提交任务立即执行
public class CachedThreadPoolTest { public static void main(String[] args) { //1.创建一个大小为5的线程池 ExecutorService threadPool= Executors.newCachedThreadPool(); //2.使用线程池执行任务一 for (int i=0;i<5;i++){ //给线程池添加任务 threadPool.submit(() -> System.out.println("线程名"+Thread.currentThread().getName()+"在执行任务1")); } //2.使用线程池执行任务二 for (int i=0;i<8;i++){ //给线程池添加任务 threadPool.submit(() -> System.out.println("线程名"+Thread.currentThread().getName()+"在执行任务2")); } } }
3、创建单个线程的线程池
public class SignleThreadPoolTest { public static void main(String[] args) { //1.创建一个大小为5的线程池 ExecutorService threadPool= Executors.newSingleThreadExecutor(); //2.使用线程池执行任务一 for (int i=0;i<5;i++){ //给线程池添加任务 threadPool.submit(() -> System.out.println("线程名"+Thread.currentThread().getName()+"在执行任务1")); } //2.使用线程池执行任务二 for (int i=0;i<8;i++){ //给线程池添加任务 threadPool.submit(() -> System.out.println("线程名"+Thread.currentThread().getName()+"在执行任务2")); } } }
4、创建基于定时任务的线程池
public class ScheduledThreadPoolTest { public static void main(String[] args) { //1.创建一个大小为5的线程池 ExecutorService threadPool= Executors.newScheduledThreadPool(10); //2.使用线程池执行任务一 for (int i=0;i<5;i++){ //给线程池添加任务 threadPool.submit(() -> System.out.println("线程名"+Thread.currentThread().getName()+"在执行任务1")); } //2.使用线程池执行任务二 for (int i=0;i<8;i++){ //给线程池添加任务 threadPool.submit(() -> System.out.println("线程名"+Thread.currentThread().getName()+"在执行任务2")); } } }
二、线程池创建源码分析
1、 参数说明:
(1)corePoolSize:核心线程数,也是线程池中常驻的线程数,线程池初始化时默认是没有线程的,当任务来临时才开始创建线程去执行任务
(2)maximumPoolSize:最大线程数,在核心线程数的基础上可能会额外增加一些非核心线程,需要注意的是只有当workQueue队列填满时才会创建多于corePoolSize的线程(线程池总线程数不超过maxPoolSize)
(3)keepAliveTime:非核心线程的空闲时间超过keepAliveTime就会被自动终止回收掉,注意当corePoolSize=maxPoolSize时,keepAliveTime参数也就不起作用了(因为不存在非核心线程);
(4)unit:keepAliveTime的时间单位
workQueue:用于保存任务的队列,当池子里的工作线程数大于corePoolSize时,这时新进来的任务会被放到队列中
(5)threadFactory:创建线程的工厂类,默认使用Executors.defaultThreadFactory(),也可以使用guava库的ThreadFactoryBuilder来创建
(6)handler:线程池无法继续接收任务(队列已满且线程数达到maximunPoolSize)时的饱和策略,取值有AbortPolicy、CallerRunsPolicy、(7)DiscardOldestPolicy、DiscardPolicy
无论以何种方式创建线程池,初始化线程池参数都会调用ThreadPoolExecutor类的构造器方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
实际的赋值操作如下:
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.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
2、四种创建方式参数比较:
(1)newCachdThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
通过参数可以看到,创建大小不限制大小的线程池,核心线程数初始化为0,因为线程池创建完成,没有提交任务的话,没有任务。线程池的最大大小为
@Native public static final int MAX_VALUE = 0x7fffffff;
换算下来是2^31-1,线程空闲时间默认是60秒。队列是同步消息队列没有拒绝策略。
(2)newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
创建线程池,初始化参数的时候,核心线程数,最大线程数都是一样的。线程空闲停留时间是0s,队列是链表实现的阻塞消息队列。
3、创建单个线程的线程池
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
核心线程数和最大线程数是一样的,空闲时间是0s,队列是阻塞消息队列,类型Rrunnable,线程停留时间是毫秒。
4、创建基于定时任务调度的线程池
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
传入参数是核心线程数,其他参数在队列中设置,最大线程数没有做限制,线程空闲时间是0s,队列是延迟消息队列。
综上所述,四种创建线程池的比较归纳如下:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。