文章目录
- 一. 线程池初了解
- 1. 什么是线程池
- 2. 使用线程池的好处
- 二. 线程池再了解
- 1. ThreadPoolExecutor类的重要配置
- 2. 线程池的工作流程
- 3. 使用Java标准库创建线程
- 三. 模拟实现简易线程池
一. 线程池初了解
1. 什么是线程池
线程池是一种采用池化思想(同理还有常量池,内存池等)来管理和复用线程的技术。线程池中有若干已经创建的线程和一个存放执行任务的阻塞队列,当队列中有待执行的任务时线程可以直接取出并执行,对于已经执行完任务的线程并不会直接销毁,而是进行适当地复用以处理更多的多线程任务,从而达到提高程序性能 和 降低系统开销的目的。
2. 使用线程池的好处
- 提高程序的响应速度:线程池中管理了若干已经创建的线程,当新任务到来时无需进行线程的创建就可对任务进行处理。
- 降低系统的开销:线程池可以对已经创建的线程进行复用,避免了频繁创建和销毁线程带来的系统开销。
- 提高线程的可管理性:线程池可以对线程进行统一的分配、调优和监控。
二. 线程池再了解
在Java中,ExecutorService 是管理线程池和所提交的任务的重要接口,它表示一个线程池示例,通过该接口的方法我们可以了解线程池基本方法的使用(如下表)。
其中 ThreadPoolExecutor类 是 ExecutorService接口的一个主要实现类,获得线程池主要有两种方法:(1)直接实例化ThreadPoolExecuto类,并手动对ThreadPoolExecuto类进行参数配置(2)通过Executors这个工厂类的工厂方法获取ThreadPoolExecuto类。
ExecutorService的主要方法:
1. ThreadPoolExecutor类的重要配置
若通过直接实例化的方式获取 ThreadPoolExecutor类,可通过以下参数对线程池进行相关的配置。
- corePoolSize(核心线程数):当线程池空闲时,即线程池中队列没有待执行任务时仍然保留的线程数量。
- maximumPoolSize(最大线程数):线程池允许存在的最大线程数,当待处理任务减少时,部分线程可能会被销毁,即 corePoolSize <= 线程数量 <= maximumPoolSize。
- keepAliveTime(最长空闲存活时间):当线程数量大于核心线程数时,多余线程被允许存活的最长时间,超过该时间未执行新任务的多余线程将被销毁。
- unit(时间单位):keepAliveTime参数的时间单位。
- workQueue(工作队列):传递任务的阻塞队列,可根据不同需求指定不同的队列类型;若对任务的优先级有要求,可选择PriorityBlockingQueue,若任务数量变动较大,可选择LinkedBlockingQueue等。
- threadFactory(创建线程的工厂):使用 ThreadFactory工厂类来创建线程,可以自动对线程相关属性进行设置。
- handle(拒绝策略):当阻塞队列中待处理的任务数量达到队列的最大容量时,对继续添加的任务采取适当的拒绝策略(4种)。
2. 线程池的工作流程
3. 使用Java标准库创建线程
在Java标准库中,主要通过 Executors这个工厂类的4个主要方法来获取线程池 ThreadPoolExecutor类 的实例,但方法的返回值为 ThreadPoolExecutor类 的实现接口 ExecutorService。(方法简介如下)
使用示例如下:
Executors.newCachedThreadPool()
public static void main(String[] args) throws InterruptedException {
// 创建一个 线程数目动态变化的 线程池
// 当线程池中的任务数 较少时,会适当的销毁线程;当待执行的任务数较多时, 会适当地创建更多 新的线程以执行任务
ExecutorService service = Executors.newCachedThreadPool();
for (int i = 0; i < 20; i++) {
int id = i;
service.submit(() -> {
System.out.println("i = " + id);
});
}
}
部分执行结果如下:
==========分割线
Executors.newSingleThreadPool():队列任务为顺序执行
public static void main(String[] args) throws InterruptedException {
// 创建一个 只有一个线程 执行线程任务的线程池
// 线程池每次只能执行 一个线程任务, 多余的任务会在任务队列中等待执行
ExecutorService service = Executors.newSingleThreadExecutor();
for (int i = 0; i < 20; i++) {
int id = i;
service.submit(() -> {
System.out.println("i = " + id);
});
}
}
三. 模拟实现简易线程池
实现线程池,需要有一个阻塞队列来存放执行任务和若干线程执行任务,并在队满时继续提交任务执行一定的拒绝策略(这里采用阻塞等待的方式)。
具体的步骤如下:
- 在线程池实例化时创建一定数量的线程,每个线程的工作内容是利用循环的方式扫描是否有待执行任务,若有则取出队列任务并执行。
- 创建固定容量的阻塞队列(ArrayBlockingQueue)用于接收添加任务。
- 为线程池提供提交任务的方法。
具体代码如下:
class MyThreadPool {
// 用来存放线程池中 要执行的任务
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(50);
public MyThreadPool(int n) {
for (int i = 0; i < n; i++) {
Thread t = new Thread(() -> {
// 每个线程采用死循环的方式扫描是否有待执行的任务
while (true) {
try {
Runnable task = queue.take();
task.run();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
}
}
// 向线程池中添加任务
public void submit(Runnable runnable) throws InterruptedException {
queue.put(runnable);
}
}
以上就是本篇文章的全部内容了,如果这篇文章对你有些许帮助,你的点赞、收藏和评论就是对我最大的支持。
另外,文章的不足之处,也希望你可以给我一点小小的建议,我会努力检查并改进。