在单个线程使用过程中遇到的问题(new Thread().start
):
- 线程的频繁创建与销毁
- 线程执行数据多且高频,频繁CPU上下文切换,造成CPU的资源浪费
以上种种问题,让我们不禁想到,怎么复用线程,引入池技术
例如:连接池,对象池,线程池
池化技术
核心就是资源复用,让空闲的线程得以复用
线程池需要满足哪些条件,才能带到复用?
- 初始化时创建一系列的线程
- 让线程不结束,通过阻塞队列实现,类似生产者消费者模型+加阻塞队列(线程安全的)
- 需要的时候唤醒某个线程执行run方法
- 队列满了的情况下,增加消费者线程,动态扩容
- 如果扩容还执行不过来,还应具有拒绝策略
- 报错,直接往上抛异常。AbortPolicy:丢弃任务并抛出RejectedExecutionException,这是线程池的默认拒绝策略
- 直接丢弃 DiscardPolicy
- 直接调用run方法(newCachedThreadPool用) CallerRunsPolicy
- 移除头部任务(等待最久的任务),然后把新的任务加入 DiscardOldestPolicy
- 存起来不被回收,需要自定义实现
Java中的线程池
1.参数详解
public ThreadPoolExecutor(int corePoolSize, //核心线程数
int maximumPoolSize, //最大线程数
long keepAliveTime, //工作线程(非核心线程)存活时间
TimeUnit unit, //单位
BlockingQueue<Runnable> workQueue, //存储任务的阻塞队列
ThreadFactory threadFactory, //初始化线程工厂
RejectedExecutionHandler handler) {} //拒绝策略
- 线程存活时间概定,run方法执行完毕即结束。工作线程回收方式,从队列中阻塞取任务超时后即结束,可以被回收
2.固定线程池newFixedThreadPool
- 核心线程数 = 最大线程数
- 阻塞队列用的是 LinkedBlockingQueue
- 拒绝策略,使用默认的AbortPolicy报错
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
3.单个线程池newSingleThreadExecutor
- 就一个线程
- 阻塞队列用的是 LinkedBlockingQueue
- 拒绝策略,使用默认的AbortPolicy报错
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
4. 缓存线程池 newCachedThreadPool
- 来多少执行多少的线程池
- SynchronousQueue对了,无结构不做存储的阻塞队列,没有消费者时生产这将阻塞
- 拒绝策略默认的AbortPolicy报错
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
5.定时线程池 newScheduledThreadPool
- 由ScheduledThreadPoolExecutor实现,最大 Integer.MAX_VALUE
- 阻塞队列DelayedWorkQueue延时队列
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
Java中使用线程池
以固定线程池的使用为例,其他的都大同小异。
ExecutorService threadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+",执行");
});
}
//结果
pool-1-thread-2,执行
pool-1-thread-2,执行
pool-1-thread-3,执行
pool-1-thread-1,执行
Java已有的线程池如无法匹配当前业务,应当自行定制最合理的线程池
线程池源码分析
- 构造方法
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;
}
- AtomicInteger ctl ,重点介绍一下这个成员变量,根据这个标识记录了锁标识和当前线程池中的线程数量,是线程安全的整形变量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//线程池中活动的线程数(低29位) -> 29
private static final int COUNT_BITS = Integer.SIZE - 3;
//最大的工作线程数量 -> 536870911
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
//线程池的各种状态 -> -536870912
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 static int runStateOf(int c) { return c & ~CAPACITY; }
//当前正在工作的线程数
private static int workerCountOf(int c) { return c & CAPACITY; }
//使用了unsafe类及其中的compareAndSwapInt方法保证线程的安全性
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
线程池中采取了原子的Integer标识,并且将32位的整数分为两部分,将原子类的AtomicInteger的二进制位数(32位),拆分高3位(表示线程池的运行状态)和低29位(表示工作线程数量)
1.execute 执行一个任务
//分三步进行
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
//获取当前ctl
int c = ctl.get();
//如果工作的线程少于核心线程 那直接新开一个线程
//从这里得知 初始化核心线程都是延迟初始化的
if (workerCountOf(c) < corePoolSize) {
//新开一个线程 并把当前Runnable交给它执行
if (addWorker(command, true))
return;
c = ctl.get();
}
//第二步 判断线程池运行状态 并把任务添加到阻塞队列中
//offer添加成功返回true,反之false
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);
}
//如果无法对任务排队,则尝试添加新任务 工作线程
else if (!addWorker(command, false))
reject(command);
}
2.addWorker()添加一个任务
//保存核心线程Worker的set
private final HashSet<Worker> workers = new HashSet<Worker>();
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);
//已有的线程数量大于或者等于最大容量 或者 核心容量 则直接返回false
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//用cas添加一个线程
//return ctl.compareAndSet(expect, expect + 1);
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 {
//任务用Worker包装需要执行的任务
//Worker中两个变量firstTask thread(new 一个线程)
w = new Worker(firstTask);
//worker中new出来的线程
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)) {
//如果t线程已经执行 抛异常
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
//将Worker保存到set中
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
//如果有任务添加成功 则马上启动工作线程
if (workerAdded) {
t.start(); //接下来来到Worker中run方法
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
3.worker类启动工作线程
//实现了线程Runnable 继承了线程并发AQS
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
//构造方法 存储当前需要执行的线程 同时new一个线程
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
}
上面有任务时,会包装成一个worker,里面有两个变量。
- firstTask:保存需要执行的任务
- thread:从上面看出是直接new了一个线程
然后在添加成员变量workers成功后,会直接启动new的那个线程,那我们直接来worker类中找run方法
//Worker#run
public void run() {
runWorker(this);
}
final void runWorker(Worker w) {
//当前线程
Thread wt = Thread.currentThread();
//获取执行任务 addWorker添加
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // 先释放锁,允许中断 allow interrupts
boolean completedAbruptly = true;
try {
//获取任务 getTask()在下面分析
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 {
//调用执行任务的run方法
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 {
//如果执行过程中出现异常 移除worker线程
processWorkerExit(w, completedAbruptly);
}
}
4.worker类中getTask()获取待执行任务
不用想,肯定会有阻塞
- workQueue
//初始化时定义的阻塞队列 前面通过workQueue.offer(command)添加任务
private final BlockingQueue<Runnable> workQueue;
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;
//(工作线程大于最大线程 || 有存活时间&&超时了)
//&& (工作线程大于1 || 队列空) 工作线程正在跑最后一个任务
//则直接减掉一个工作线程
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
//return ctl.compareAndSet(expect, expect - 1)
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
//根据是否有存活时候从阻塞队列中 阻塞取出 || 阻塞超时取出
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
//超时后如果还是null说明队列为空 当前线程声明周期结束 跳出循环 返回null
//没有核心线程的标识 能否成为核心线程取决于谁执行在最后
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
5.processWorkerExit() worker线程执行完成或异常处理
private void processWorkerExit(Worker w, boolean completedAbruptly) {
//如果是意外退出则workerCount--
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
//加锁统计执行的任务数 并移除WorkerSet中的Worker
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//记录执行的任务次数 然后重worker set中移除
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}
//判断线程池状态 根据状态是否需要终止线程
tryTerminate();
int c = ctl.get();
//如果线程池状态是小于stop的 非停止状态
if (runStateLessThan(c, STOP)) {
//如果没有意外退出 需要根据是否运行核心线程数存活标识来是否销毁线程
if (!completedAbruptly) {
//如果设置了KeepAliveTime之后,核心线程也会被销毁
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
//如果核心线程最少保留0且队列不为空的情况下
//线程池并没停止 则保留一个工作线程
if (min == 0 && ! workQueue.isEmpty())
min = 1;
//如果当前的工作线程总数大于需要的Worker数 那就直接返回
if (workerCountOf(c) >= min)
return; // replacement not needed
}
//新建一个worker 核心线程
addWorker(null, false);
}
}
源码总结
执行一个任务时,如果当前工作线程小于核心线程,那么直接新初始化一个核心线程(Worker),存储在成员变量 HashSet workers里面,如果添加成功则会启动worker中的线程。
如果工作线程大于核心线程,判断线程池的运行状态并使用offer()(添加成功返回true,反之false)添加到阻塞队列中,并检查线程池运行状态,如不在运行状态则移除该任务并调用拒绝策略,如果正常但是工作线程为0时则新建一个无任务的工作线程
如果**offer()**添加失败(阻塞队列已满)则直接添加工作线程,如果线程池有效线程数小于maximumPoolSize则直接新增工作线程执行,如果线程池已满则调用拒绝策略。
worker实现了线程Rannable同时继承了AQS,在run方法中循环去阻塞队列中getTask()获取需要执行的任务,获取到任务后直接run方法执行,这里使用的是AQS中的互斥锁
getTask()获取任务,是根据是否设置存活时间或者当前工作线程数量大于核心线程数来决定是阻塞还是带超时时间的阻塞,如果是超时阻塞就说明队列为null,那么就可以销毁该工作线程了
如果设置了KeepAliveTime,核心线程也会被销毁(设置了allowCoreThreadTimeOut为true),但如果队列不为空但是线程数为0时,则重新在新建一个worker
以上就是本章的全部内容了。
上一篇:Java本地线程变量ThreadLocal的神秘面纱
下一篇:线程池工作线程ForkJoin的使用
皇皇三十载,书剑两无成