什么是线程池?
池,那应该就是有很多的预计创建好线程,等待被使用了
其实创建线程是相对来说是很耗费资源的,而且创建和销毁一般都是成对的出现的,如果没有合理的创建和销毁,
那么是会很影响系统的性能的,会过度消耗系统资源,最终可能导致系统资源不足,甚至导致系统不可用等问题。
为了解决这个问题,就有只能提前创建好一定数量的线程,并且统一管理。
因此就又来线程池的概念,需要使用就从线程池去分配,使用完之后不会被销毁等待下一次任务分配。增加了线程的重复创建导致不必要的开销。
总之一句话就是 : 线程池负责统一管理线程,负责分配线程的
线程作用以及场景
作用就是其优势,优势就是有其应用场景了
1. 降低创建线程和销毁线程的性能开销
2. 提高响应速度,当有新任务需要执行是不需要等待线程创建就可以立马执行
3. 合理的设置线程池大小可以避免因为线程数超过硬件资源瓶颈带来的问题
线程池小Demo
java中涉及到线程池的相关类均在jdk1.5开始的java.util.concurrent包中,涉及到的几个核心类及接口包括:Executor、Executors、ExecutorService、ThreadPoolExecutor、FutureTask、Callable、Runnable等。
一般都是用 ExecutorService,它是Java提供的用于管理线程池的接口。该接口的两个作用:控制线程数量和重用线程
package com.yyp.ThreadDemo;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author lamb-yyp
* @version 1.0
* @date 2020/9/26 16:11
*/
public class TestThreadPool implements Runnable {
// 默认创建 5个线程
private static ExecutorService service = Executors.newFixedThreadPool(5);
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程池...线程:" +Thread.currentThread().getName());
}
public static void main(String[] args) {
for (int i=0;i< 50 ;i++){
service.execute(new TestThreadPool());
}
service.shutdown();
}
}
结果:
常见的几个线程池:
1、newCachedThreadPool():可缓存线程池,先查看池中有没有以前建立的线程,如果有,就直接使用。如果没有,就建一个新的线程加入池中,缓存型池子通常用于执行一些生存期很短的异步型任务并且每一个空闲线程会在 60 秒后自动回收
2、newFixedThreadPool(int n):该方法返回一个固定数量的线程池,线程数不变,当有一个任务提交时,若线程池中空闲,则立即执行,若没有,则会被暂缓在一个任务队列中,等待有空闲的线程去执行。
3、newScheduledThreadPool(int n):创建一个可以指定线程的数量的线程池,但是这个线程池还带有延迟和周期性执行任务的功能,类似定时器
4、newSingleThreadExecutor():创建一个线程的线程池,若空闲则执行,若没有空闲线程则暂缓在任务队列中。保证只有一个线程执行
以上这些线程都是通过Executors 构建的。
如图:
线程池原理浅析
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public ThreadPoolExecutor(int corePoolSize, // 线程数(核心)
int maximumPoolSize, // 最大线程数(核心+辅助)
long keepAliveTime, // 存活时间(辅助线程存活时间)
TimeUnit unit,//存活时间
BlockingQueue<Runnable> workQueue, // 保存任务执行的队列
ThreadFactory threadFactory,// 创建线程工厂
RejectedExecutionHandler handler)// 任务拒绝是执行处理方式
{...}
线程池的逻辑流程图
addWorker 主要有2个作用:1、创建线程且执行线程,2、原子操作(cas),计算线程数。
线程池主要工作内容:1、判断是否已达到最大线程数
2、当前线程池是否在工作
3、封装线程加入集合中去
4、启动
5、添加失败,重新计算线程数,以及移除失败的线程。
那么应该会有人疑问,怎么执行任务呢??
其实创建Worker时,本身就是实现了Runable,那么也重写了run();
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
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);
}
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. */
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); // 执行线程
}
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
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 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);
}
}
// 获取任务,从阻塞队列中获取任务执行
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.
// 1、线程池shutdown、stop,且为空
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 {
//根据 timed 来判断,如果为 true,则通过阻塞队列 poll 方法进行超时控制,如果在keepaliveTime 时间内没有获取到任务,则返回 null.则通过 take 方法阻塞式获取队列中的任务
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
疑问:1、为什么会继承AQS不是创建ReentrantLock(可以乘重入)
原因:因为一个任务在执行时,应该是不允许中途停止的,要不就任务执行失败,要不就成功,因此当获取锁的时候,就不允许重入,如果该线程不是独占状态,那么就是说明处于空闲状态,没有任务执行
2、this.firstTask = firstTask; // 真正的任务
this.thread = getThreadFactory().newThread(this);
原因:每个 worker,都是一条线程,同时里面包含了一个 firstTask,即初始化时要被首先执行的任务. 最终执行的任务是执行runWorker(this);
思路:我们都是到如果线程池创建了n个线程,那么如果线程结束的话,那么就会销毁的,那么如何保证线程池的线程数呢,那么就不让线程终止,那就需要阻塞,这也就是需要一开始创建线程池时的时候那几个参数了(核心线程数,最大线程数,以及存活时间等参数)这就是为了在线程空余的时候控制核心线程数,以及销毁空闲的线程
拒绝策略:
1、AbortPolicy:直接抛出异常,默认策略;
2、CallerRunsPolicy:用调用者所在的线程来执行任务;
3、DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
4、DiscardPolicy:直接丢弃任务;
当然也可以根据应用场景实现 RejectedExecutionHandler 接口,自定义饱和策略,如记录
日志或持久化存储不能处理的任务
线程池注意事项
1、线程池的构建不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式。因为用 Executors 使得用户不
需要关心线程池的参数配置,意味着大家对于线程池的运行规则也会慢慢的忽略。时间久的话可能导致OOM或者CPU占有过高
2、合理的设置线程池的大小,分析下需要用到线程执行的方式IO密集型还是CPU密集型
3、线程池的关闭,ThreadPoolExecutor 提供了两个方法,用于线程池的关闭,分别是 shutdown()和shutdownNow(),其中:shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务 shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务