文章目录
线程池与线程对比
线程
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
Long start = System.currentTimeMillis();
final Random random = new Random();
final List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 100000; i++) {
Thread thread = new Thread() {
@Override
public void run() {
list.add(random.nextInt());
}
};
thread.start();
thread.join();
}
System.out.println("时间:" + (System.currentTimeMillis() - start));
System.out.println("大小:" + list.size());
}
}
打印结果:
时间:25126
大小:100000
线程池
public class ThreadPoolTest {
public static void main(String[] args) throws InterruptedException {
Long start = System.currentTimeMillis();
final Random random = new Random();
final List<Integer> list = new ArrayList<Integer>();
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 100000; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
list.add(random.nextInt());
}
});
}
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.DAYS);
System.out.println("时间:"+(System.currentTimeMillis() - start));
System.out.println("大小:"+list.size());
}
}
打印结果:
时间:54
大小:100000
线程执行时间如此之长的原因:线程执行完后需要进行频繁的上下文切换,浪费CPU资源。
为什么要用线程池?
线程池做的工作主要是控制运行的线程的数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其它线程执行完毕,再从队列中取出任务来执行。
线程池的主要特点为:线程复用;控制最大并发数;管理线程。
优点:
第一:降低资源消耗。通过重复利用已创建的线程来降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
常用线程池的比较
-
Executors.newFixedThreadPool(int):
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
- 固定线程数量的线程池
- 可控制线程最大并发数,超出的部分在阻塞队列中等待
- 创建的线程池corePoolSize和maximumPoolSize值是相等的
- 使用LinkedBlockingQueue 阻塞队列(由链表组成的双向阻塞队列)
- 适合于执行长期的任务,性能好很多
-
Executors.newSingleThreadExecutor():
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
- 单线程化的线程池
- 只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序执行
- 创建的线程池corePoolSize和maximumPoolSize都为1,它使用的LinkedBlockingQueue 阻塞队列
- 适合于一个任务一个任务执行的场景
-
Executors.newCachedThreadPool():执行很多短期异步的小程序或者负载较轻的服务器
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
- 自适应线程数量的线程池/可缓存线程池
-
将线程池corePoolSiz设置为了0,maximumPoolSize为
Integer.MAX_VALUE
,keepAliveTime为60s- 使用的SynchronousQueue阻塞队列,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程
了解:
-
Executors.newScheduledThreadPool(),带时间调度的线程池
-
Executors.newWorkStealingPool(int):java8新增,使用目前机器上可用的处理器作为它的并行
三种常见创建线程池的方法,用哪个多?
一个都不用!
阿里开发手册规定:
- 线程资源必须通过线程池来提供,不允许显式的创建线程;
- 线程池不允许使用Executors创建,而应该使用ThreadPoolExecutor的方式,这样的处理方式可以更明确线程池的运行规则,规避资源耗尽的风险。
为什么不能用Executors创建线程池呢?
- fixedThreadPool和singleThreadPool的阻塞队列使用的都是LinkedBlockiingQueue,该阻塞队列默认是无界的,允许的最大请求队列长度是Integer.MAX_VALUE,这会堆积大量的请求,从而导致OOM;
- cachedThreadPool和scheduledThreadPool允许创建的线程数量是Integer_MAX_VALUE,可能会创建大量线程,从而导致OOM。
线程池7大参数
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后,就会把到达的任务放到阻塞队列中;
-
MaximumPoolSize
指的是线程池中的最大线程数,只有当阻塞队列满了不能在添加任务时,这个参数才会生效;
-
KeepAliveTime
指的是超出corePoolSize以外的空闲线程的最大存活时间,只要时间一到,这些空闲线程会被销毁;
-
TimeUnit
指的是KeepAliveTime的时间单位
-
WorkQueue
WorkQueue就是阻塞队列,当线程池中的线程数达到corePoolSize后,就会把到达的任务放到阻塞队列中;
-
ThreadFactory
线程工厂,用来创建线程,一般默认即可;
-
RejectHandler
指的是拒绝策略,当线程池中的线程数达到最大线程数并且阻塞队列也满时,将执行拒绝策略;。
为什么要用阻塞队列,普通队列不可以吗?
- 当任务队列中没有任务时,阻塞队列可以阻塞获取任务的线程,使得线程进入wait状态,并释放CPU资源;阻塞队列自带阻塞与唤醒的功能,不需要额外进行处理;
- 一般的队列只能保证作为一个有限长度的缓冲区,如果超出了缓冲长度,就无法保留当前的任务了; 而阻塞队列可以通过阻塞来保留当前想要持续入队的任务;。
拒绝策略
当阻塞队列被排满,同时线程池中线程数达到了最大线程数,即无法继续处理新来的任务,这时就需要拒绝策略机制来处理。
JDK内置拒绝策略(以下内置策略均实现了RejectedExecutionHandler接口)
- AbortPolicy(默认):直接抛出RejectedExecutionException异常阻止系统正常运行
- CallerRunsPolicy:调用者运行,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退给调用者,从而降低新任务的流量
- DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务
- DiscardPolicy:直接丢弃任务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的一种方案
当线程数到达了核心线程数后,为什么新的请求进来要先添加到队列中而不是直接创建新的线程?
因为在创建新线程时需要获取全局锁,因此这个时候其他线程都得阻塞,从而大大影响整体效率。
线程池工作原理
- 创建线程池之后,等待提交过来的请求;
- 当调用execute()方法添加任务请求时,会先将正在运行的线程数与核心线程数作比较:
- 如果小于核心线程数,就马上创建新的线程来执行请求;
- 如果等于或者大于核心线程数,就会将新的请求加入到阻塞队列中;
- 如果阻塞队列满了,就将正在运行的线程数与最大线程数作比较:
- 如果小于最大线程数就创建非核心线程来执行新的任务;
- 如果是大于或者等于最大线程数的话,就会执行拒绝策略;
- 当一个线程执行完手头任务后,就会从阻塞队列中取出下一个任务继续执行;
- 当一个线程执行完任务后无所事事,线程池就会将正在运行的线程数量和核心线程数作比较,如果大于核心线程数的话,一旦到达设置好的KeepAliveTime后,就会将这个线程销毁;
- 当所有线程都执行完任务时,线程池会销毁多余的线程,重新收缩到corePoolSize的大小;。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TN5XbAkh-1651598868288)(E:\file\学习\课后总结\并发编程\assert\ThreadPoolExecutor.png)]
ThreadPoolExecutor源码分析
类结构
public interface Executor {...}
public interface ExecutorService extends Executor {...}
public abstract class AbstractExecutorService implements ExecutorService {...}
public class ThreadPoolExecutor extends AbstractExecutorService {...}
- Executor : 是最顶层接口,它只包含一个方法execute();
- ExecutorService : 继承自Executor接口,在Executor接口基础上添加了**submit()**方法,并添加了若干线程池状态操作方法,如shutdown(),shutdownNow()等;
- AbstractExecutorService : 继承自ExecutorService,并实现了ExecutorService的若干方法,如submit()等;
- ThreadPoolExecutor : 继承于AbstractExecutorService,是线程池的核心类,几乎所有与线程池有关的逻辑都封装在这个类中;。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m73ZYKGe-1651598868290)(E:\file\学习\课后总结\MySQL\assert\ThreadPoolExecutor1.png)]
运行状态
/**
* @since 1.5
* @author Doug Lea
*/
public class ThreadPoolExecutor extends AbstractExecutorService {
// 原子整数
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 32-3
private static final int COUNT_BITS = Integer.SIZE - 3;
// (2^29)-1(约5亿)
// 000111111111111111111111111111111
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 线程池运行状态被存储在高3位
// 11100000000000000000000000000000
private static final int RUNNING = -1 << COUNT_BITS;
// 00100000000000000000000000000000
private static final int SHUTDOWN = 0 << COUNT_BITS;
// 00100000000000000000000000000000
private static final int STOP = 1 << COUNT_BITS;
// 01000000000000000000000000000000
private static final int TIDYING = 2 << COUNT_BITS;
// 01100000000000000000000000000000
private static final int TERMINATED = 3 << COUNT_BITS;
/* 对ctl进行打包和拆包 */
// ~CAPACITY结果为11100000000000000000000000000000,它与c做与运算得到的就是当前线程池的状态
private static int runStateOf(int c) { return c & ~CAPACITY; }
// CAPACITY低29位全1高3位全0,它与c做与运算得到的就是当前运行的线程个数
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
. . . . . .
}
我们来分析一下:
-
ctl是一个原子整数,它的低29位为线程池中处于RUNNING状态的线程个数,高3位为线程池所处的状态,来表示是否正在运行、关闭等;
为了将它们打包为一个int,将workerCount限制为(229)-1个线程,而不是(231)-1(20亿)个线程。如果这在将来成为问题,可以将变量更改为原子长度,并调整下面的移位/掩码常数。但在需要之前,使用int会更快、更简单。
workerCount是允许启动和不允许停止的worker数量,该值可能暂时不同于活动线程的实际数量,例如,当ThreadFactory在请求时无法创建线程,以及退出的线程在终止前仍在执行簿记时,用户可见的池大小为工作集的当前大小。
-
COUNT_BITS表示的是RUNNING状态下的线程数对应二进制的位数,Integer.SIZE - 3,也就是29;
-
CAPACITY用于与ctl做与运算,得到RUNNING状态下线程的个数;
-
RUNNING表示线程池的运行状态,也是线程池的默认状态,可处理新任务,也可以执行队列中的任务;
-
SHOTDOWN表示线程池的关闭态,当调用ThreadPoolExecutor实例的showdown()方法之后,这个ThreadPoolExecutor实例就会进入关闭态,关闭态能够对阻塞队列中的任务进行处理,不能够接受新添加的非空任务,但是可以接受新添加的空任务;
-
STOP表示线程池的停止态,当调用ThreadPoolExecutor实例的shutdownNow()方法之后,这个ThreadPoolExecutor实例就会进入停止态。停止态不能接受新添加任务,也不能够对阻塞队列中的任务进行处理,并且会中断正在运行的任务;
-
TIDYING表示线程池的整理态,所有任务已经结束,workerCount = 0 ,将执行terminated()方法;
-
TERMINATED表示线程池的结束态,当线程池处于整理态,并调用terminated()方法,执行完毕之后,就会进入结束态,此状态也表示整个线程池生命周期的结束,
线程池状态关系图例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9oTzHEJy-1651598868293)(E:\file\学习\课后总结\MySQL\assert\ThreadPoolExecutor2.png)]
execute()
/**
* 在将来的某个时候执行给定的任务。任务可以在新线程或现有池线程中执行。如果由于此执行器已关闭或已达到其容量而无法提交任务执行,则该任务将由当前的RejectedExecutionHandler处理。
*/
public void execute(Runnable command) {
// 任务为空,抛出NPE
if (command == null)
throw new NullPointerException();
//获取ctl的值
int c = ctl.get();
// 获取当前线程数量与核心线程数作比较
if (workerCountOf(c) < corePoolSize) {
// 创建新线程并提交任务
if (addWorker(command, true))
return;
// 刷新c
c = ctl.get();
}
// 如果线程池状态为运行状态,并且成功加入到工作队列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 如果线程池状态在任务加入队列后变成了非运行状态,并且从工作队列移除任务成功
if (! isRunning(recheck) && remove(command))
// 则执行拒绝策略
reject(command);
else if (workerCountOf(recheck) == 0)
// 新增一个线程并创建一个null任务
addWorker(null, false);
}
// 通过addWorker()方法新增一个线程
else if (!addWorker(command, false))
reject(command);
}
进入execute()方法:
-
首先判断任务是否为空,如果为空则抛出空指针异常;
-
然后通过ctl.get()获取ctl的值,再通过workerCountOf()方法获取当前线程数量,并与核心线程数作比较;
// CAPACITY低29位全1高3位全0,它与c做与运算得到的就是当前运行的线程个数 private static int workerCountOf(int c) { return c & CAPACITY; }
-
如果小于核心线程数则通过addWorker()方法创建一个新的线程并提交任务:
- 如果创建成功则return,代表成功提交任务;
- 如果创建失败,则刷新c的值;
-
如果第一步没有完成任务的提交,那么便需要将任务添加到工作队列中;
-
首先通过isRunning()方法判断线程池状态是否为运行态,然后将任务添加到工作队列中;
-
如果添加成功则再进行一次check,如果线程池状态在任务加入队列后变成了非运行状态(有可能是在执行到这里线程池shutdown了) ,那么就需要对该任务执行拒绝策略;
final void reject(Runnable command) { handler.rejectedExecution(command, this); }
-
如果通过了双检,则判断当前工作线程数是否为0,如果是则新增一个线程并创建一个null任务,任务在堵塞队列存在了就会从队列中取出 这样做的意义是保证线程池在running状态必须有一个任务在执行;
-
-
如果加入工作队列也失败了,就尝试通过addWorker()方法新增一个线程;
-
如果失败,则表示线程池中线程数量已经达到了最大线程数,或者线程池已经关闭了,因此执行拒绝策略;
同时也可以看出,拒绝策略不仅仅是在饱和状态下使用, 在线程池进入到关闭阶段同样需要使用到;。
addWorker()
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
//获取ctl
int c = ctl.get();
//获取线程池状态
int rs = runStateOf(c);
//线程状态非运行并且非shutdown状态任务为空队列非空就不能新增线程了
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
//获取线程数
int wc = workerCountOf(c);
//如果当前线程数大于等于最大线程数,return false,结束addWorker()方法;
if (wc >= CAPACITY ||
//如果大于等于核心线程数并且core为true时,return false;
//如果大于等于最大线程数并且core为false时,return false;
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//ctl+1 工作线程池数量+1 如果成功就跳出死循环。
if (compareAndIncrementWorkerCount(c))
break retry;
//自增失败,刷新c
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 {
//失败回退 从wokers移除w 线程数减1 尝试结束线程池
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
-
首先通过runStateOf()获取线程池状态;
// ~CAPACITY结果为11100000000000000000000000000000,它与c做与运算得到的就是当前线程池的状态 private static int runStateOf(int c) { return c & ~CAPACITY; }
-
下面这段代码乍一看很绕,其实它想表达的意思就是当线程池状态为关闭态时任务为空或不为空并且工作队列为空时,不再添加任务;当线程池状态为关闭态并且工作队列不为空时,可以添加空任务;
这是因为如果阻塞队列为空,整个线程池不会再新建线程并且不会再往队列添加新任务,所以没必要在添加空任务;但是如果阻塞队列不为空,有可能已经没有线程,关闭态的线程池需要把阻塞队列的任务消费完成,所以需要新建线程来消费任务。因此添加一个空任务的目的就是为了新建一个新线程来消费阻塞队列中的任务。
if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) return false;
-
进入下一段代码
- 首先获取当前线程池中正在运行的线程数;
- 如果当前线程数大于等于最大线程数,return false,结束addWorker()方法;
- 如果大于等于核心线程数并且core为true时,return false;
- 如果大于等于最大线程数并且core为false时,return false;
int wc = workerCountOf(c); if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) return false;
-
再往下走,工作线程池数量+1 如果成功就跳出死循环。
if (compareAndIncrementWorkerCount(c)) break retry;
- ctl是AtomicInteger类型变量,采用了CAS操作保证了ctl自增的线程安全性;
- 如果ctl自增失败,则有可能是其他线程做了自增操作;
- 因此需要继续更新c的值,并继续执行内层死循环做ctl自增操作;
private boolean compareAndIncrementWorkerCount(int expect) { return ctl.compareAndSet(expect, expect + 1); }
-
在继续执行内层死循环之前,要先判断线程池的运行状态是否发生了变化,因此先执行外层死循环,重新做状态判断;
// else CAS failed due to workerCount change; retry inner loop if (runStateOf(c) != rs) continue retry;
-
以上主要是对ctl工作线程+1;接下来开始添加新任务;
-
首先定义了两个boolean类型变量workerStarted表示线程是否已启动,workerAdded表示线程是否已经添加到线程池;Worker类型变量w表示此次要添加的线程,初始化为null;
boolean workerStarted = false; boolean workerAdded = false; Worker w = null;
-
初始化w并获取刚创建的线程;
w = new Worker(firstTask); final Thread t = w.thread;
-
然后对t进行判空,如果不为空则获取全局可重入锁,并加锁;
final ReentrantLock mainLock = this.mainLock; mainLock.lock();
-
再获取线程池运行状态rs,然后判断如果线程池处于运行态或者处于关闭态但提交任务为空,则可以做添加线程操作;
-
再判断线程是否已经启动,如果已经启动,抛出IllegalThreadStateException异常;
-
将Worker实例加入线程池workers中;
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(); }
/** * Set containing all worker threads in pool. Accessed only when * holding mainLock. */ private final HashSet<Worker> workers = new HashSet<Worker>();
-
如果线程已经被加入线程池,则启动线程,并置标志位workerStarted为true,表示线程已经启动;
if (workerAdded) { t.start(); workerStarted = true; }
-
如果线程没有启动成功,执行addWorkerFailed()开启回滚操作;
if (! workerStarted) addWorkerFailed(w);
private void addWorkerFailed(Worker w) { //使用mainLock加锁 final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { //如果w不为空的话,从集合中remove掉该w if (w != null) workers.remove(w); //ctl减一 decrementWorkerCount(); tryTerminate(); } finally { mainLock.unlock(); } }
Worker
Worker类用来维护运行任务的线程的中断控制状态,以及其他次要的功能。
Worker简化了AQS获取和释放围绕每个任务执行的锁的过程。这可以防止唤醒等待任务的工作线程而不是中断正在运行的任务的中断。
我们实现了一个简单的不可重入互斥锁,而不是使用可重入锁,因为我们不希望工作任务在调用诸如setCorePoolSize之类的池控制方法时能够重新获取锁。
此外,为了在线程实际开始运行任务之前抑制中断,我们将锁定状态初始化为负值,并在启动时清除它(在runWorker中)。
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/**
* This class will never be serialized, but we provide a
* serialVersionUID to suppress a javac warning.
*/
private static final long serialVersionUID = 6138294804551838833L;
/** Thread this worker is running in. Null if factory fails. */
//正在运行woker线程
final Thread thread;
/** Initial task to run. Possibly null. */
//传入的任务
Runnable firstTask;
/** Per-thread task counter */
//完成的任务数(监控用)
volatile long completedTasks;
/**
* Creates with given first task and thread from ThreadFactory.
* @param firstTask the first task (null if none)
*/
Worker(Runnable firstTask) {
//防止线程被中断
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** Delegates main run loop to outer runWorker */
public void run() {
runWorker(this);
}
// Lock methods
//
// The value 0 represents the unlocked state.
// The value 1 represents the locked state.
protected boolean isHeldExclusively() {
return getState() != 0;
}
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
Worker类继承了AQS抽象类并且实现了Runnable接口。
-
我们来分析它的构造方法,它首先调用了setState(-1),这样做的目的是为了在线程实际开始运行任务之前抑制中断,在启动时会清除它(在runWorker中)
protected final void setState(int newState) { state = newState; }
-
然后用提交的Runnable任务对成员变量firstTask进行赋值;
this.firstTask = firstTask;
-
然后调用ThreadFactory的newThread()来创建新线程并对thread进行赋值;
可以看到该线程并不是通过任务来构造的,而是通过Worker对象自身来构造的。
为什么这么设计呢?原因很简单,假如以任务来建立线程的话,那以后从阻塞队列再消费任务的时候,就会读一个任务建一个线程,这样做的话,也就失去了线程池的意义;
而以Worker实例来建立线程的话,可以在Worker线程的run()方法内调用任务的run()方法,从而实现线程复用;
this.thread = getThreadFactory().newThread(this);
public Thread newThread(Runnable r) { Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); if (t.isDaemon()) t.setDaemon(false); if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY); return t; }
runWorker()
/** Delegates main run loop to outer runWorker */
public void run() {
runWorker(this);
}
主要worker运行循环。重复地从队列中获取任务并执行它们,同时处理许多问题:
1。我们可以从一个初始任务开始,在这种情况下,我们不需要得到第一个任务。否则,只要池在运行,我们就可以从getTask获取任务。如果返回null,则工作进程将由于池状态或配置参数的更改而退出。其他退出源于外部代码中的异常抛出,在这种情况下completedjustly保持不变,这通常会导致processWorkerExit替换该线程。
2.在运行任何任务之前,获取锁以防止任务执行时其他池中断,然后我们确保除非池停止,否则该线程没有中断设置。
3.每个任务运行之前都会调用beforeExecute,这可能会引发异常,在这种情况下,我们会导致线程在不处理任务的情况下死亡(使用CompletedTruntly true中断循环)。
4.假设beforeExecute正常完成,我们运行任务,收集其抛出的任何异常以发送给afterExecute。我们分别处理RuntimeException、Error(这两个规范都保证我们可以捕获)和任意丢弃。因为我们无法在Runnable内重新播放丢弃的内容。运行时,我们将它们包装在退出时的错误中(到线程的UncaughtExceptionHandler)。任何抛出的异常也会保守地导致线程死亡。
5.任务完成后。运行完成后,我们调用afterExecute,它也可能引发异常,这也会导致线程死亡。根据JLS第14.20节,此例外情况将生效,即使任务已完成。快跑投球。异常机制的最终效果是,afterExecute和线程的UncaughtExceptionHandler具有尽可能准确的信息,我们可以提供有关用户代码遇到的任何问题的信息。
final void runWorker(Worker w) {
//获取当前线程
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
//把state从-1改为0 意思是从现在开始允许中断
w.unlock();
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
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 {
processWorkerExit(w, completedAbruptly);
}
}
- 首先获取当前线程wt以及提交的任务task,并将w的firstTask设置为空;
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
- 然后把state从-1改为0,表明从现在起允许中断;
w.unlock();
- 然后判断如果提交的任务不为空,则处理提交的任务;如果提交的任务为空,则通过getTask()从阻塞队列获取任务;
while (task != null || (task = getTask()) != null)
- 然后首先加全局锁
w.lock();
public void lock() { acquire(1); }
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
//入队成功
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//又又又尝试获取锁~
final Node p = node.predecessor();
//如果当前刚入队节点的前置节点是头节点,那么有必要再次尝试获取锁,因为head很可能是刚初始化的head,或者也可能head马上就会释放锁
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//是否应该挂起当前结点
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
//取消获取锁
cancelAcquire(node);
}
}
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
- 如果当前线程池状态等于stop 就中断
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
- 接着执行
task.run();
-
执行完任务之后,将task重新设置为空,等下次循环就会从队列里面获取
完成任务数+1
finally {
task = null;
w.completedTasks++;
w.unlock();
}
getTask
我们回头再来看getTask方法
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;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
- 定义timedOut变量,表示当前线程是否已经过期
boolean timedOut = false;
- 获取线程池运行状态
int c = ctl.get();
int rs = runStateOf(c);
- 如果线程池处于STOP、TIDYING、TERMINATED三种状态之一,或者处于SHUTDOWN状态且阻塞队列为空时,就递减ctl的workerCount字段并返回空任务
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
private void decrementWorkerCount() {
do {} while (! compareAndDecrementWorkerCount(ctl.get()));
}
- 如果不是,则获取当前运行的线程个数;timed变量用来标志超时销毁,主要分为两种情况:1、是否允许核心线程过期;2、当前运行线程个数是否大于核心线程阈值;
int wc = workerCountOf(c);
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
- 相当于如果满足wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty()),则将当前运行线程数减一,如果减一成功,返回null;如果减一失败,则往下继续执行;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
- 最后根据是否执行过期策略(timed),以不同的方式(poll和take)从阻塞队列获取Runnable,如果获取任务r非空,则返回r;如果获取任务为空,则说明已经到了过期时间,timedOut = true,那么在下一次循环时,线程数减一,退出方法;。
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
a
private void decrementWorkerCount() {
do {} while (! compareAndDecrementWorkerCount(ctl.get()));
}
* 如果不是,则获取当前运行的线程个数;timed变量用来标志超时销毁,主要分为两种情况:1、是否允许核心线程过期;2、当前运行线程个数是否大于核心线程阈值;
```Java
int wc = workerCountOf(c);
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
- 相当于如果满足wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty()),则将当前运行线程数减一,如果减一成功,返回null;如果减一失败,则往下继续执行;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
- 最后根据是否执行过期策略(timed),以不同的方式(poll和take)从阻塞队列获取Runnable,如果获取任务r非空,则返回r;如果获取任务为空,则说明已经到了过期时间,timedOut = true,那么在下一次循环时,线程数减一,退出方法;。
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}