线程池
“线程池(英语:thread pool):一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。但是阿里的开发手册中不推荐使用Executors 来创建线程池
阿里开发手册摘取内容如下:
【强制】 线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这
样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明: Executors 返回的线程池对象的弊端如下:
1) FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM(内存溢出)。
2) CachedThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE, 可能会创建大量的线程,从而导致 OOM(内存溢出)。
Executors 工具类
常用方法(创建线程池的方法)
返回值 | 方法名 | 说明 |
---|---|---|
ExecutorService | newFixedThreadPool(int nThreads) | 创建一个线程池对象, nThreads是线程池内的线程最大数量 |
例如创建线程池: ExecutorService pool = Executors.newFixedThreadPool(3);
实例代码:
// 创建线程池对象 -- 参数是线程池的大小(线程数)
ExecutorService pool = Executors.newFixedThreadPool(3);
// 利用线程池执行线程任务
// execute(); 参数需要Runnable接口类型
for (int i = 0; i < 3; i++) {
pool.execute(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " : " + i);
}
}
});
}
ThreadPoolExecutor创建线程池
构造方法
方法 | 说明 |
---|---|
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) |
corePoolSize 核心线程数 maximumPoolSeze 最大线程数 keepAliveTime 线程空闲多长时间被释放 unit 时间单位 wordQueue 任务队列 |
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) |
与上面构造方法多了一个参数threadFactory 线程工厂 |
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) |
handler 拒绝执行任务的一种策略 |
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) |
包含了上面所有的参数 |
其最大吞吐量为: 最大线程数 + 最大队列数
代码案例-简单案例
public static void main(String[] args) {
// 任务队列. 这里设置为2
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2);
// 定义线程工厂, 可以自动以线程名字前缀
ThreadFactory threadFactory = new ThreadFactory() {
AtomicLong al = new AtomicLong(1); // 自动递增策略, 从1开始
@Override
public Thread newThread(Runnable r) {
String name = "cgb2004-thread-" + al.getAndIncrement(); // 先获取值, 再自增
return new Thread(r, name);
}
};
ThreadPoolExecutor tExecutor = new ThreadPoolExecutor(
2, // corePoolSize 核心线程数
3, // maximumPoolSeze 最大线程数
60, // keepAliveTime 线程空闲多长时间被释放
TimeUnit.SECONDS, // unit 时间单位
workQueue, // wordQueue 任务队列
threadFactory, // threadFactory 线程工厂
new CallerRunsPolicy()); // 拒绝执行任务的一种策略
tExecutor.execute(new Runnable() { // 从线程池获取线程
@Override
public void run() {
String tName = Thread.currentThread().getName();
System.out.println(tName + "->task-01");
// try{Thread.sleep(10000);} catch(Exception e) {}
}
});
tExecutor.execute(new Runnable() { // 从线程池获取线程
@Override
public void run() {
String tName = Thread.currentThread().getName();
System.out.println(tName + "->task-02");
// try{Thread.sleep(10000);} catch(Exception e) {}
}
});
// tExecutor.execute(new Runnable()...
}
执行流程描述
首先, 核心线程数corePoolSize 即线程池中一直存在线程的数量, 当需要时会从里面取出线程使用
当核心线程都在使用中, 那么就会加入到消息队列中如, 等核心线程空闲的时候, 队列中的任务才能执行
当核心线程都在使用中, 且消息队列也达到了最大上限, 这时候又有任务来了, 那么就会创建新的线程, 来执行, 前提是线程池中的线程数量没有超过最大线程数
如果说最大线程数也满了, 这时候又来了新的任务, 这时候会拒绝执行, 抛出异常, 当然, 我们也可以使用构造器中的RejectedExecutionHandler handler
借口自定义拒绝执行的的策略, 例如传入一个new CallerRunsPolicy()
参数, 来让main线程执行等