认识线程池:
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。
如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。
超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。
线程池的执行流程:
关于上述图片的文字描述:
- 提交任务后,线程池先判断线程数是否达到了核心线程数(corePoolSize)。如果未达到线程数,则创建核心线程处理任务;否则,就执行下一步;
- 随后线程池判断任务队列是否满了。如果没满,则将任务添加到任务队列中;否则,执行下一步;
- 随后因为任务队列满了,线程池就判断线程数是否达到了最大线程数。如果未达到,则创建非核心线程处理任务;否则,就执行饱和策略,默认会抛出RejectedExecutionException异常。
对于饱和策略的说明:
当任务队列和最大线程满了时所采取的应对策略,默认是AbordPolicy,表示无法处理新任务,并抛出RejectedExecutionException异常。
除此之外的三种策略:
- CallerRunsPolicy:用调用者所在的线程处理任务。此策略提供简单的反馈机制,能够减缓新任务的提交速度。
- DiscardPolicy:不能执行任务,并将任务删除。
- DiscardOldestPolicy:丢弃队列最近的任务,并执行当前的任务。
关于线程池处理流程的代码描述:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//1.当前池中线程比核心数少,新建一个线程执行任务
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//2.核心池已满,但任务队列未满,添加到队列中
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);
}
//3.核心池已满,队列已满,试着创建一个新线程
else if (!addWorker(command, false))
reject(command); //如果创建新线程失败了,说明线程池被关闭或者线程池完全满了,拒绝任务
}
线程池的基本参数:
继承关系:
Java中已经提供了创建线程池的一个类:Executor
而我们创建时,一般使用它的子类:ThreadPoolExecutor.
继承关系:
public class ThreadPoolExecutor extends AbstractExecutorService
实现接口:
Executor, ExecutorService
ThreadPoolExcutor的构造参数和作用:
ThreadPoolExcutor的构造参数和作用:
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits 运行状态
private static final int RUNNING = -1 << COUNT_BITS;
//接受新任务并且处理已经进入队列的任务
private static final int SHUTDOWN = 0 << COUNT_BITS;
//不接受新任务,但是处理已经进入队列的任务
private static final int STOP = 1 << COUNT_BITS;
//不接受新任务,不处理已经进入队列的任务,并且中断正在执行的任务
private static final int TIDYING = 2 << COUNT_BITS;
//所有任务执行完成,workerCount为0.线程赚到了状态TIDYING会执行terminated()构造方法
private static final int TERMINATED = 3 << COUNT_BITS;
//terminated()已经执行完成,状态之间可以相互转化
ThreadPoolExecutor构造方法中的参数:
corePoolSize、maximunPoolSize、keepAliveTime、unit、workQueue、threadFactory和handler。下面分别介绍这个几个参数:
corePoolSize
//核心线程的数量。默认是没有超时的,也就是说就算线程闲置,也不会被处理。
//但是如果设置了allowCoreTimeOut为true,那么当核心线程闲置时,会被回收。
//maximumPoolSize:最大线程池尺寸,被CAPACITY限制(2^29-1)。
keepAliveTime:
//闲置线程被回收的时间限制
Unit:
//keepAliveTime的单位
workQueue:
//用于存放任务的队列
threadFactory:
//创建线程的工厂类
handler:
//当任务执行失败时,使用handler通知调用者
四种线程池的详细介绍:
newCachedThreadPool //创建一个可缓存线程池程
newCachedThreadPool,是一种线程数量不定的线程池,并且其最大线程数为Integer.MAX_VALUE,这个数是很大的,一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。但是线程池中的空闲线程都有超时限制,这个超时时长是60秒,超过60秒闲置线程就会被回收。调用execute将重用以前构造的线程(如果线程可用)。这类线程池比较适合执行大量的耗时较少的任务,当整个线程池都处于闲置状态时,线程池中的线程都会超时被停止。
newFixedThreadPool //创建一个定长线程池
newFixedThreadPool 创建一个指定工作线程数量的线程池,每当提交一个任务就创建一个工作线程,当线程 处于空闲状态时,它们并不会被回收,除非线程池被关闭了,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列(没有大小限制)中。由于newFixedThreadPool只有核心线程并且这些核心线程不会被回收,这样它更加快速底相应外界的请求。
newSingleThreadExecutor //创建一个单线程化的线程池
newSingleThreadExecutor这类线程池内部只有一个核心线程,以无界队列方式来执行该线程,这使得这些任务之间不需要处理线程同步的问题,它确保所有的任务都在同一个线程中按顺序中执行,并且可以在任意给定的时间不会有多个线程是活动的。
newScheduledThreadPool //创建一个定长线程池
newScheduledThreadPool 创建一个线程池,它的核心线程数量是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置时会被立即回收,它可安排给定延迟后运行命令或者定期地执行。这类线程池主要用于执行定时任务和具有固定周期的重复任务。
上述 线程池学习 的PPT 下载
提取码:ked1
深入源码:
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
//在某些情况下用来存储任务,并将任务提供给线程池中的工作线程
private final BlockingQueue<Runnable> workQueue;
//用来对pooSize、corePoolSize、maximumPoolSize、runState、workers修改时候同步
private final ReentrantLock mainLock = new ReentrantLock();
//线程池中所有线程的集合,访问和修改需要mainLock的配合
private final HashSet<Worker> workers = new HashSet<Worker>();
//用来支持waitTemination
private final Condition termination = mainLock.newCondition();
//跟踪线程池中线程的最大值,具体的猜测是为了矫正poolsize,访问和修改需要配合mainLock
private int largestPoolSize;
//已完成任务的数量,在任务处于Terminate状态时才更新,访问和修改需要mainLock的配合
private long completedTaskCount;
/*
* 一下参数都是用户控制的,全部被声明为了Volatile类型的值,这样能够确保在多线程下,每个
* 线程都能够获取到最新值。
*/
//线程工厂,用户可以自定义,以便在想线程池创建线程时附加一些个人操作
private volatile ThreadFactory threadFactory;
//当线程池处于shutdown或者处于饱和时执行的拒绝策略
private volatile RejectedExecutionHandler handler;
//设置线程池中空闲线程等待多时毫秒被回收
private volatile long keepAliveTime;
//指定线程池中的空闲线程是否一段时间被回收,false一直存活
private volatile boolean allowCoreThreadTimeOut;
//核心线程池大小,若allowCoreThreadTimeOut被设置,全部空闲超时被回收的情况下会为0
private volatile int corePoolSize;
//最大线程池大小,不得超过CAPACITY
private volatile int maximumPoolSize;
//默认的拒绝策略
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
//间接调用最后一个构造函数,采用默认的拒绝策略AbortPolicy和默认的线程工厂
ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue<Runnable>)
//间接调用最后一个构造函数,采用默认的默认的线程工厂
ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue<Runnable>,
RejectedExecutionHandler)
//间接调用最后一个构造函数,采用默认的拒绝策略AbortPolicy
ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue<Runnable>, ThreadFactory)
//前面三个分别调用了最后一个
ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue<Runnable>, ThreadFactory, RejectedExecutionHandler)
//最后一个构造函数的具体实现
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {
//参数合法性检验,核心线程数目、最大线程数目、线程空闲回收时间不得小于0,最大线程池不得小于核心线程数数目
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;//拒绝策略
}
//提交函数
public Future<?> submit(Runnable task)
public <T> Future<T> submit(Runnable task, T result)
public <T> Future<T> submit(Callable<T> task)
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
//addWorker方法
private boolean addWorker(Runnable firstTask, boolean core) {
//(1)循环CAS操作,将线程池中的线程数+1.
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);
//core true代表是往核心线程池中增加线程 false代表往最大线程池中增加线程
//线程数超标,不能再添加了,直接返回
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//CAS修改clt的值+1,在线程池中为将要添加的线程流出空间,成功退出cas循环,失败继续
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
//如果线程池的状态发生了变化回到retry外层循环
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
//(2)新建线程,并加入到线程池workers中。
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
//对workers操作要通过加锁来实现
final ReentrantLock mainLock = this.mainLock;
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
//细化锁的力度,防止临界区过大,浪费时间
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int c = ctl.get();
int rs = runStateOf(c);
//判断线程池的状态
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();
//修正largestPoolSize的值
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
//线程添加线程池成功,则开启新创建的线程
if (workerAdded) {
t.start();//(3)
workerStarted = true;
}
}
} finally {
//线程添加线程池失败或者线程start失败,则需要调用addWorkerFailed函数,如果添加成功则需要移除,并回复clt的值
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
总结:
在什么情况下使用线程池?
1.单个任务处理的时间比较短
2.将需处理的任务的数量大
使用线程池的好处:
1.减少在创建和销毁线程上所花的时间以及系统资源的开销
2.有效控制线程池的最大并发数,避免大量的线程之间因抢占系统资源而阻塞
3.能够对线程进行简单的管理,并提供一下特定的操作如:可以提供定时、定期、单线程、并发数控制等功能
为什么要用线程池:
1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程越多,消耗的内存就越大)。