目录
一. 什么是线程池
二. 线程池的作用
三. java提供的线程池类
四. ThreadPoolExecutor的构造方法及参数理解
1. int corePoolSize: 核心线程数.
2. int maximumPoolSize: 最大线程数 = 核心线程数 + 非核心线程数
3. int keepAliveTime:非核心线程允许空闲的最大时间.
4. BlockingQueue workQueue: 工作队列.
5. ThreadFactory threadFactory: 工厂模式
6. RejectedExecutionHandle handle: 拒绝策略. (最重要)
五. 线程池的核心方法
六. 创建线程池的简化代码
七. 模拟实现线程池
八. 总结
一. 什么是线程池
线程池是一种并发处理机制, 它预先创建一定数量的线程, 并按照一定的策略管理和组织这些线程, 当有任务需要执行时, 线程池会从空闲线程中取出一个线程来执行任务, 执行完毕后, 这个线程又重新回到线程池中, 等待下一轮分配.
二. 线程池的作用
避免频繁的创建和销毁线程, 减少资源消耗.
那么我们为什么认为线程池会减少资源的消耗呢?
a.从线程池中获取线程, 通过程序代码即可实现, 是可控的.
b. 通过操作系统创建线程, 需要操作系统内核配合完成, 是不可控的.
c. 使用内核, 就需要一段代码切换到内核中, 这个过程会消耗资源.
因此, 使用线程池就可以减少应用程序切换到内核中的开销.
三. java提供的线程池类
ThreadPoolExecutor类 继承 AbstractExecutototService抽象类.
AbstractExecutototService抽象类 实现 ExecutorService接口.
ExecutotService接口 拓展 Executor接口.
四. ThreadPoolExecutor的构造方法及参数理解
面试题: 解释ThreadPoolExecutor类构造方法的参数分别是什么含义?
1. int corePoolSize: 核心线程数.
核心线程数就是线程池中的最少线程数, 线程池一创建, 核心线程也随之创建, 直到线程池销毁, 这些核心线程才销毁.
2. int maximumPoolSize: 最大线程数 = 核心线程数 + 非核心线程数
(非核心线程数是自适应的, 繁忙时创建, 不繁忙时销毁, 线程数不是越多越好)
3. int keepAliveTime:非核心线程允许空闲的最大时间.
(一旦非核心线程空闲的时间超过设定的时间, 它就会被优化).
TimeUnit unit: 时间单位. (枚举)
4. BlockingQueue<Runnable> workQueue: 工作队列.
(本质上也是生产者消费者模型, 调用submit就是在生产任务, 线程池中的线程就是在消费任务)
5. ThreadFactory threadFactory: 工厂模式
(也是一种设计模式, 用来弥补构造方法的缺陷)
在线程池中的作用: 设置线程池中的线程类型(核心线程/非核心线程)
6. RejectedExecutionHandle handle: 拒绝策略. (最重要)
为什么要使用拒绝策略: 队列满了, 调用submit添加任务使线程阻塞(没有真的阻塞), 这时就无法做别的事情的, 不是一个好的选择, 于是就需要使用拒绝策略. 使线程不阻塞.
AbortPolicy: 线程池直接抛出异常.
CallerRunsPolicy: 让调用submit的线程自己执行任务.
DiscardOldestPolicy: 丢弃最老的线程, submit当前线程.
DiscardPolicy: 丢弃submit的这个任务.
五. 线程池的核心方法
submit(Runnable): 通过Runnable来描述一段任务, 使用submit将这段任务提交到线程池中, 线程池再分配空闲线程来执行这段任务.
六. 创建线程池的简化代码
ExecutorService executorService = Executors.newFixedThreadPool(4);
Executors.newFixedThreadPool(nThreads); // 最大线程数和核心线程数一样.(创建固定线程数目的线程池)
ExecutorService executorService = Executors.newCachedThreadPool();
最大线程数是一个很大的数字. (线程池中的线程数可以增加)
七. 模拟实现线程池
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
class MyThreadPool {
BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(10);
public MyThreadPool(int n) {
// 创建固定线程数量的线程池
// 创建n个线程
for (int i = 0; i < n; i++) {
Thread thread = new Thread(() -> {
try {
while (true) {
Runnable runnable = blockingQueue.take();
runnable.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
// System.out.println(n);
}
}
public void submit(Runnable runnable) throws InterruptedException {
blockingQueue.put(runnable);
}
}
public class demo4 {
static int count = 0;
public static void main(String[] args) throws InterruptedException {
MyThreadPool myThreadPool = new MyThreadPool(4);
for (int i = 0; i < 100; i++) {
myThreadPool.submit(() -> {
System.out.println(Thread.currentThread().getName() + " count " + count);
});
}
}
}
八. 总结
1. 什么是线程池. (线程池是一种并发处理机制, 它预先创建出一定数量的线程, 并使用一定的策略进行管理和组织. 当有任务需要执行时, 线程池从空闲线程出取出一个线程来执行这个任务. 执行完毕后, 这个线程又重新回到线程池中, 等待先一轮分配).
2. 线程池的作用, 避免频繁创建和销毁线程, 减少资源消耗.
(从线程池中拿取线程是应用程序级别的, 创建一个线程是操作系统内核级别的.
内核级别操作消耗的资源 > 应用程序级别消耗的资源 ==> 不可控消耗的资源 > 可控消耗的资源)
3. java提供的线程池类
4. 线程池的构造方法及参数理解. (corePoolSize, maximumPoolSize, keepAliveTime, BlockingQueue<Runnabke>, ThreadFactory, RejectedExceptionHandle)
5. 线程池的核心方法 submit(Runnable). (向线程池中提交任务, 线程池会分配空闲线程来执行).
6. 创建线程池的简化代码. (Executors.newFixedThreadPool(n) 创建具有固定线程数量的线程池
Executors.newCachedThreadPool() 创建不限制线程数量的线程池).
7. 补充: 使用shutdown方法来强制结束核心线程.