🔍 开发者资源导航 🔍 |
---|
🏷️ 博客主页: 个人主页 |
📚 专栏订阅: JavaEE全栈专栏 |
线程池
线程池是一种 多线程处理技术,它通过 预先创建并管理一组线程,在程序运行时 复用这些线程
来执行任务,而不是为每个任务都创建和销毁新线程。线程池是 Java 并发编程 的核心组件之一,广泛应用于高并发、高性能场景。
为什么要有线程池?
-
线程创建和销毁开销大:频繁创建线程会消耗 CPU 和内存资源。
-
线程数量失控风险:如果无限制创建线程,可能导致系统资源耗尽(如 OutOfMemoryError)。
-
任务调度优化:线程池可以更高效地管理任务队列,提高 CPU 利用率。
线程池的应用场景
(1) 适合使用线程池的场景
✅ Web 服务器(如 Tomcat 处理 HTTP 请求)
✅ 数据库连接池(管理数据库查询任务)
✅ 异步任务处理(如日志记录、邮件发送)
✅ 定时任务调度(如每天凌晨执行数据备份)
(2) 不适合使用线程池的场景
❌ 长时间阻塞的任务(如死循环任务,可能导致线程池耗尽)
❌ 对响应时间要求极高的任务(线程池的任务调度会有一定延迟)
ThreadPoolExecutor
ThreadPoolExecutor是Java中内置的线程池的实现类。
它包含的线程数可以动态调整
,在任务多的时候自动扩容成更多的线程,任务少的时候,把额外的线程干掉,节省资源。
四种构造方法
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
创建具有给定的初始参数和默认线厂和拒绝执行处理程序的一个新的 ThreadPoolExecutor。
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)
创建具有给定的初始参数和默认线厂一个新的 ThreadPoolExecutor。
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)
创建一个新的 ThreadPoolExecutor与给定的初始参数和默认拒绝执行处理程序。
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
创建具有给定的初始参数的一种新 ThreadPoolExecutor。
关键参数
CorePoolSize
:线程核心数,至少拥有的线程数量,线程池一创建它们也随之创建,直到线程池销毁它们才会销毁;maxnumPoolSize
:最大线程核心数量,线程核心数量 = 线程核心数+非核心线程数,其中这些非核心线程是自适应的,它们在繁忙的时候创建,不繁忙的时候销毁;keepAliveTime
:非核心线程允许空闲的最大时间,如果非核心线程超过了这个时间还没有任务可以分配,就会销毁这个非核心线程。unit
:keepAliveTime的时间单位,是一个枚举类型的值。workQueue
:工作队列,线程池本质上也是一个生产者消费者模型,可以选择使用数组/链表,指定capacity,指定是否要带有优先级和比较规则;ThreadFactory
:线程的工厂模式,可以指定线程的创建方式。hander
:拒绝策略,面试中最常考的一个参数,已知线程池本质上也是一个生产者消费者模型,因此他也会有线程塞满的时候,对于线程池来说如果他满了是不会触发入队列操作的,所以我们需要给它安排一个拒绝策略,线程池指定的策略仅有四种;
策略 | 说明 |
---|---|
AbortPolicy(默认) | 直接抛出异常 RejectedExecutionException |
CallerRunsPolicy | 由提交任务的线程自己执行该任务 |
DiscardPolicy | 直接丢弃任务,不做任何处理 |
DiscardOldestPolicy | 丢弃队列中最旧的任务,然后重新提交新任务 |
我们可以将这些参数比喻成
线程池=>公司
核心线程=>正式员工
非核心线程=>实习生
非核心线程允许空闲的最大时间=>允许实习生摸鱼的时间
(希望可以帮助到你的理解)
要点解析
工厂模式
:属于设计模式的一种,因为构造方法的名称是固定的,要想提过不同的版本,就需要通过重载,但是有时候不一定可以构成重载。
工厂方法的核心,通过静态方法,把构造对象new的过程,各种属性初始化的过程封装起来了。
提供多组静态方法,根据不同情况的构造。
例如一个点的坐标可以根据x,y来确定,也可以根据其到原点的距离r以及角度aph来确定,但是因为他们的参数类型都是double的所以无法在创建时实现两种并存。
因此我们可以用工厂模式来弥补构造方法的缺陷
class point {
}
//举个例子,不一定是这么写的
class pointFactory {
public static point makePointByxy(double x, double y) {
point res = new point();
return res;
}
public static point makePointByr(double r, double aph) {
point res = new point();
return res;
}
}
在线程池的ThreadFactory参数就是应用了此种模式,但是它只是一个接口,具体需要我们来实现。
ThreadFactory threadFactory = new ThreadFactory() {
private int cnt = 0;
@Override
public Thread newThread(Runnable r) {
//指定线程名字
Thread thread = new Thread(r, "Thread" + cnt++);
//设为后台进程
thread.setDaemon(true);
return thread;
}
};
线程池的工厂类
在上述参数中我们不难发现其构造十分麻烦,因此Java也给出了几种简单的构造方法。
使用 Executors 工厂类创建线程池。
// 固定大小的线程池(核心线程=最大线程)
ExecutorService fixedPool = Executors.newFixedThreadPool(10);
// 可缓存的线程池(无核心线程,线程数可动态增长)
ExecutorService cachedPool = Executors.newCachedThreadPool();
// 单线程池(只有一个线程,保证任务顺序执行)
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
// 定时任务线程池
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(5);
手写一个线程池
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
class MyThreadPoll {
private BlockingQueue<Runnable> queue = null;
public MyThreadPoll(int n) {
queue = new ArrayBlockingQueue<>(1000);
//最大线程数量
for (int i = 0; i < n; i++) {
Thread t = new Thread(()->{
while (true) {
try {
Runnable r = queue.take();
r.run();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
}
}
public void submit(Runnable r) throws InterruptedException{
queue.put(r);
}
}
public class MyThreadPollMain {
public static void main(String[] args) throws InterruptedException {
MyThreadPoll threadPoll1 = new MyThreadPoll(10);
for (int i = 0; i < 100; i++) {
int id = i;
threadPoll1.submit(()->{
System.out.println(Thread.currentThread().getName() + ":" + id);
});
}
}
}
感谢各位的观看Thanks♪(・ω・)ノ,如果觉得满意的话留个关注再走吧。