线程池和数据库连接池非常类似,可以统一管理和维护线程,减少没有必要的开销。
因为频繁的开启线程或者停止线程,线程需要从新被 cpu 从就绪到运行状态调度,需要发生
cpu 的上下文切换(每次都要耗费服务器资源与时间),效率非常低。
在实际的开发项目中禁止自己new线程
发短信、发邮件。量大的用mq
核心点:复用机制 提前创建好固定的线程一直在运行状态 实现复用 限制线程创建数量。
1.降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。
2.提高响应速度:任务到达时,无需等待线程创建即可立即执行。
3.提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。
4.提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池 ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。
可通过jdk自带的方式创建,比如Executors,有以下几种:
Executors.newCachedThreadPool(); 可缓存线程池
Executors.newFixedThreadPool();可定长度 限制最大线程数
Executors.newScheduledThreadPool() ; 可定时
Executors.newSingleThreadExecutor(); 单例
本质思想:创建一个线程,不会立马停止或者销毁,而是一直存在实现复用。
1.提前创建固定大小的线程一直保持在正在运行状态(可能会非常消耗cpu资源)
2.当需要线程执行任务,将该任务提交缓存在并发队列中;如果缓存队列满了,则会执行拒绝策略。
3.正在运行的线程从并发队列中获取任务从而实现多线程复用问题。
package com.han.hjqdemo1.entity.d0228;
import java.util.ArrayList; import java.util.List; import java.util.concurrent.BlockingDeque; import java.util.concurrent.LinkedBlockingDeque;
/** * @Author: hanjinqun * @DateTime: 2022/2/28 * @Description: 手写线程池 */ public class Test2 {
private List workThreads;
private BlockingDeque runnableBlockingDeque;//并发队列
private boolean flag = true;
/**
* maxThreadCount 最大线程数
* dequeSize 最大并发队列数
*/
public Test2(int maxThreadCount, int dequeSize) {
runnableBlockingDeque = new LinkedBlockingDeque(dequeSize);
workThreads = new ArrayList(maxThreadCount);
for (int i = 0; i < maxThreadCount; i++) {
new WorkThread().start();
}
}
class WorkThread extends Thread {
@Override
public void run() {
while (flag || runnableBlockingDeque.size() > 0) {
Runnable runnable = runnableBlockingDeque.poll();
if (runnable != null) {
runnable.run();
}
}
}
}
public boolean execute(Runnable runnable) {
return runnableBlockingDeque.offer(runnable);
}
public static void main(String[] args) {
Test2 test2 = new Test2(2, 2);
for (int i = 0; i < 10; i++) {
final int finalI = i;
test2.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "," +
finalI);
}
});
}
test2.flag = false;
} }
返回值有可能是2次,有可能是4次。4次的原因是两个创建的线程和两个缓存在线程池的线程同时触发。
corePoolSize:核心线程数量 一直正在保持运行的线程
maximumPoolSize:最大线程数,线程池允许创建的最大线程数。
keepAliveTime:超出 corePoolSize 后创建的线程的存活时间。
unit:keepAliveTime 的时间单位。
workQueue:任务队列,用于保存待执行的任务。
threadFactory:线程池内部创建线程所用的工厂。
handler:任务无法执行时的处理器
1.当线程数小于核心线程数时,创建线程。
2.当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
3.当线程数大于等于核心线程数,且任务队列已满 。
3.1 若线程数小于最大线程数,创建线程 。
3.2 若线程数等于最大线程数,抛出异常,拒绝任务 。
4.代码示例:
package com.han.hjqdemo1.entity.d0228;
import java.util.concurrent.*;
/**
* @Author: hanjinqun
* @DateTime: 2022/3/1
* @Description: 线程池底层实现原理
*/
public class Test4 {
public static void main(String[] args) {
ExecutorService executorService = MyThreadPooolExecitor.newFixedThreadPool(2, 4, 5);
for (int i = 1; i <= 10; i++) {
final int finali = i;
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "," + finali);
}
});
}
}
}
MyThreadPooolExecitor类
package com.han.hjqdemo1.entity.d0228;
import java.util.concurrent.*;
/**
* @Author: hanjinqun
* @DateTime: 2022/3/1
* @Description: TODO
*/
public class MyThreadPooolExecitor {
public static ExecutorService newFixedThreadPool(int corePoolSize, int maxNumPoolSize, int blockingQueue) {
return new ThreadPoolExecutor(corePoolSize, maxNumPoolSize, 60L, TimeUnit.MILLISECONDS,
new LinkedBlockingDeque<>(blockingQueue), (RejectedExecutionHandler) new MyThreadPooolExecitor());
}
}
MyExecutionHandler类(自定义处理hander)
package com.han.hjqdemo1.entity.d0228;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @Author: hanjinqun
* @DateTime: 2022/3/2
* @Description: TODO
*/
public class MyExecutionHandler implements ThreadFactory, RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// System.out.print("拒绝");
// r.run();
}
@Override
public Thread newThread(Runnable r) {
return null;
}
}
可以自定义异拒绝异常,将该任务缓存到 redis、本地文件、mysql 中后期项目启动实现补偿。
1.AbortPolicy 丢弃任务,抛运行时异常
2.CallerRunsPolicy 执行任务
3.DiscardPolicy 忽视,什么都不会发生
4.DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务
5.实现 RejectedExecutionHandler 接口,可自定义处理器。
因为Executors底层都是基于 ThreadPoolExecutor 构造函数封装的,ThreadPoolExecutor构造函数中是无界的队列缓存,有可能导致线程池溢出,最大线程数失效。
再牛逼的梦想,也抵不住傻逼般的坚持。 ------酷酷的韩~