JAVA 线程池
1 线程池的优势
在多线程编程时,创建线程是十分消耗资源的,当线程创建过多时,便会引发内存溢出,因此引入了线程池技术。其实就是⼀个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。总体来说,线程池有如下的优势:
- 降低资源消耗: 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度: 当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 提高线程的可管理性: 线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
2 线程池的创建
线程池的创建有两种方式
2.1 使用ThreadPoolExecutor自定义创建线程池
部分源码:
public ThreadPoolExecutor(int corePoolSize,//核心线程数量
int maximumPoolSize,// 最大线程数
long keepAliveTime, // 最大空闲时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 任务队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 饱和处理机制
}
参数说明
- corePoolSize(必需):核心线程数。默认情况下,核心线程会一直存活,但是当将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。
- maximumPoolSize(必需):线程池所能容纳的最大线程数。当活跃线程数达到该数值后,后续的新任务将会阻塞。
- keepAliveTime(必需):线程闲置超时时长。如果超过该时长,非核心线程就会被回收。如果将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。
- unit(必需):指定 keepAliveTime 参数的时间单位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。
- workQueue(必需):任务队列。通过线程池的 execute() 方法提交的 Runnable 对象将存储在该参数中。其采用阻塞队列实现。
- threadFactory(可选):线程工厂。用于指定为线程池创建新线程的方式。
- handler(可选):拒绝策略。当达到最大线程数时需要执行的饱和策略。
2.2 使用Executors工厂来创建
常用线程池
方法 | 说明 |
---|---|
newFixedThreadPool | 创建一个可重复固定的线程数的线程池 |
newCachedThreadPool | 创建一个可缓存的线程池,调用execute将重复用以前构造的线程(如果当前线程可用)。如果没有可用线程则创建新的线程并加入到池中。终止并从缓存中移除那些已有60s未被使用的线程。 |
newSingleThreadExecutor | 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行 |
newScheduledThreadPool | 创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。 |
2.3 线程池的使用
// 创建线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE,
KEEP_ALIVE,
TimeUnit.SECONDS,
sPoolWorkQueue,
sThreadFactory);
// 向线程池提交任务
threadPool.execute(new Runnable() {
@Override
public void run() {
... // 线程执行的任务
}
});
// 关闭线程池
threadPool.shutdown(); // 设置线程池的状态为SHUTDOWN,然后中断所有没有正在执行任务的线程
threadPool.shutdownNow(); // 设置线程池的状态为 STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表
3 功能线程池
3.1 定长线程池(FixedThreadPool)
特点: 只有核心线程,线程数量固定,执行完立即回收,任务队列为链表结构的有界队列。
应用场景: 控制线程最大并发数。
示例代码
public class Demo {
public static void main(String[] args) throws InterruptedException {
ExecutorService pool = Executors.newFixedThreadPool(2);
Runnable task = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
};
// 线程池可以用来执行任务(Runnable/Callable) submit(Runnable/Callable)/execute(Runnable)
pool.submit(task);
pool.submit(task);
pool.submit(task);
// 释放资源
pool.shutdown();
// 线程池关闭后, 不能再执行新的任务
pool.submit(task);
}
}
3.2 定时线程池(ScheduledThreadPool )
特点: 核心线程数量固定,非核心线程数量无限,执行完闲置 10ms 后回收,任务队列为延时阻塞队列。
应用场景: 执行定时或周期性的任务。
示例代码
public class Demo {
public static void main(String[] args) throws InterruptedException {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
Runnable task = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
};
Object obj = new Object();
// 线程池可以用来执行任务(Runnable/Callable) submit(Runnable/Callable)/execute(Runnable)
pool.schedule(task, 5000, TimeUnit.MILLISECONDS);
// pool.submit(task);
//
// pool.submit(task);
// 释放资源
pool.shutdown();
}
}
3.3 可缓存线程池(CachedThreadPool)
特点: 无核心线程,非核心线程数量无限,执行完闲置 60s 后回收,任务队列为不存储元素的阻塞队列。
应用场景: 执行大量、耗时少的任务。
示例代码
public class Demo {
public static void main(String[] args) throws InterruptedException {
ExecutorService pool = Executors.newCachedThreadPool();
Runnable task = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
};
// 线程池可以用来执行任务(Runnable/Callable) submit(Runnable/Callable)/execute(Runnable)
pool.submit(task);
pool.submit(task);
pool.submit(task);
Thread.sleep(3000);
// 线程池中的线程可以重复使用
pool.submit(task);
pool.submit(task);
pool.submit(task);
// 如果超出了线程池中的线程数, 根据需求会创建新的线程对象
pool.submit(task);
System.out.println("主方法结束");
// 释放资源
pool.shutdown();
}
}
3.4 单线程化线程池(SingleThreadExecutor)
特点: 只有 1 个核心线程,无非核心线程,执行完立即回收,任务队列为链表结构的有界队列。
应用场景: 不适合并发但可能引起 IO 阻塞性及影响 UI 线程响应的操作,如数据库操作、文件操作等。
示例代码
public class Demo {
public static void main(String[] args) throws InterruptedException {
ExecutorService pool = Executors.newSingleThreadExecutor();
Runnable task = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
};
// 线程池可以用来执行任务(Runnable/Callable) submit(Runnable/Callable)/execute(Runnable)
pool.submit(task);
pool.submit(task);
pool.submit(task);
// 释放资源
pool.shutdown();
}
}