文章目录
线程
线程生命周期:
在Java JDK类库中的Thread类里,只定义了6种状态。
NEW:未启动状态
RUNNABLE:可运行,包括(运行和就绪状态)
WAITING:无限期等待
TIMED_WAITING:限期等待
BLOCKED:阻塞,等待锁的释放
TERMINATED:结束
线程池
生产者消费者模式
为什么要使用生产者和消费者模式
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这种生产消费能力不均衡的问题,所以便有了生产者和消费者模式。
什么是生产者消费者模式
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
这个阻塞队列就是用来给生产者和消费者解耦的。纵观大多数设计模式,都会找一个第三者出来进行解耦,如工厂模式的第三者是工厂类,模板模式的第三者是模板类。在学习一些设计模式的过程中,如果先找到这个模式的第三者,能帮助我们快速熟悉一个设计模式。
ThreadPoolExecutor
workerCount(工作线程数量)
runState(运行状态)
运行状态解释:
状态 | 解释 | |
---|---|---|
RUNNING | -1 << COUNT_BITS | 运行态,可处理新任务并执行队列中的任务 |
SHUTDOW | 0 << COUNT_BITS | 关闭态,不接受新任务,但处理队列中的任务 |
STOP | 1 << COUNT_BITS | 停止态,不接受新任务,不处理队列中任务,且打断运行中任务 |
TIDYING | 2 << COUNT_BITS | 整理态,所有任务已经结束,workerCount = 0 ,将执行terminated()方法 |
TERMINATED | 3 << COUNT_BITS | 结束态,terminated() 方法已完成 |
execute
在 ThreadPoolExecutor
类中,最核心的任务提交方法是 execute()
方法:
public void execute(Runnable command) {
//判断提交的任务是否为 null, 是则抛出异常
if (command == null)
throw new NullPointerException();
/*
* 获取线程池控制状态
* ctl 是一个 AtomicInteger 变量 (骚操作)
* jdk 8 中通过一个 int 值的前 28 位表示工作线程数量 workerCount, 剩余高位来表示 线程池状态
* 计算 workerCount 和 runState 时通过掩码计算。 CAPACITY = (1 << 29) - 1
* private static int runStateOf(int c) { return c & ~CAPACITY; }
* private static int workerCountOf(int c) { return c & CAPACITY; }
* */
int c = ctl.get();
//1. 当线程数小于 核心线程池容量时 将添加工作线程去执行任务
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get(); // 不成功则再次获取线程池控制状态
}
//2. (worker线程数量大于核心线程池容量时)如果线程池处于 RUNNING 状态,将命令加入 workQueue 队列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get(); //再次检查防止状态突变
if (! isRunning(recheck) && remove(command))
//2.1 如果状态改变,线程池没有 RUNNING 则将命令移出队列,并拒绝执行
reject(command);
else if (workerCountOf(recheck) == 0)
//2.2 状态没有改变,线程池 RUNNING,但 worker线程数量为 0, 则添加非core的worker线程
addWorker(null, false);
}
//3. 如果线程池没有 RUNNING 并尝试添加非core的 worker 线程失败,那就拒绝执行
else if (!addWorker(command, false))
reject(command);
}
addWorker()
此方法用来创建新的线程添加到线程池
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
/*
* 检查线程池状况, 确保此时可以添加新的线程,
* 如果是runing,那么跳过if。
* 如果rs>=SHUTDOWN,同时不等于SHUTDOWN,即为SHUTDOWN以上的状态,那么不接受新线程。
* 如果rs>=SHUTDOWN,同时等于SHUTDOWN,同时first != null,那么拒绝新线程,
* 如果为Empty,那么队列已空,不需要增加消耗线程,返回 false。
* */
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
/*
* 判断线程池是否已满,如果线程数大于等于最大容量 CAPACITY 直接返回false
* core 是一个boolean 参数,表明调用者想把此线程添加到哪个线程池
* 根据 core 的值判断要添加的线程池是否已满
**/
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//CAS 操作增加工作线程数
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
//CAS 操作失败, 再次检查状态重来一次
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 {
// 再次检查状态,因为状态可能在获取锁之前改变
int rs = runStateOf(ctl.get());
//确保当前线程池还接收新的线程
//结合上面的线程状态知道:当状态值大于等于 SHUTDOWN 时 线程池就不再接收新的线程了
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;
}
Executors
让我们再看看Executors提供的那几个工厂方法。
newSingleThreadExecutor
创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。
此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
new ThreadPoolExecutor(1, 1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>())
newFixedThreadPool
创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。
线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
newCachedThreadPool
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,
那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。
此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
https://blog.csdn.net/Johnnyz1234/article/details/98318528