Java并发线程之线程池的知识总结
线程池的概念
线程池是一种线程使用模式。线程池中包含了一组线程,线程池可以用来控制创建线程的数量和频率,降低了系统资源消耗率。当有新任务需要执行时,可以直接使用已经存在线程,而不是重新创建新的线程。
线程池的用途
线程池的主要作用是:
* 重用线程
* 控制线程数量,避免线程数量过多,导致系统资源的消耗和浪费
* 提高线程的创建、销毁效率
线程池的实现
Java中的线程池是通过Executor框架实现的。 Executor框架提供了一个线程池的标准接口,具体实现由ThreadPoolExecutor类实现。
ThreadPoolExecutor类的构造方法
ThreadPoolExecutor有多个构造方法,这里简单介绍最常用的一个构造方法:
public ThreadPoolExecutor(int corePoolSize, //线程池中核心线程的数量
int maximumPoolSize, //线程池中最大线程数量
long keepAliveTime, //非核心线程的空闲时间超时,超时就会被销毁
TimeUnit unit, //keepAliveTime的时间单位,通常设置为秒或者毫秒
BlockingQueue<Runnable> workQueue, //线程池中缓存任务的队列
ThreadFactory threadFactory, //线程工厂,用于创建新的线程
RejectedExecutionHandler handler) //拒绝策略,当任务添加到线程池失败后的处理方法
线程池的参数介绍
corePoolSize
表示线程池中的核心线程数量。当新任务添加到线程池中时,如果当前运行的线程数小于corePoolSize,那么就创建新的线程来处理请求。如果当前运行的线程数大于或等于corePoolSize,则把任务添加到线程池的工作队列中去。
maximumPoolSize
表示线程池中最大线程数量。当缓存队列满了之后,如果还有新的任务要处理,则会创建新的线程去处理新的任务。但是创建线程数不能超过maximumPoolSize。如果当前运行的线程数已经达到maximumPoolSize,那么就会根据拒绝策略RejectedExecutionHandler来决定如何处理新的任务。
keepAliveTime
表示非核心线程的空闲时间超时,超时就会被销毁。这里的非核心线程是指当前运行的线程数量大于corePoolSize的线程。
unit
表示keepAliveTime的时间单位,通常设置为秒或者毫秒。
workQueue
表示线程池中缓存任务的队列。当新任务添加到线程池中时,如果当前运行的线程数已经达到corePoolSize,那么新的任务就会被添加到workQueue队列中去。
threadFactory
表示创建线程的工厂,用于创建新的线程。
handler
表示拒绝策略,当任务添加到线程池失败后的处理方法。Java提供了4种拒绝策略:
* AbortPolicy(终止策略):如果无法处理新请求,则快速失败并抛出RejectedExecutionException异常。
* CallerRunsPolicy(调用者策略):调用提交任务的线程来处理该任务。
* DiscardOldestPolicy(丢弃旧任务策略):丢弃队列中等待最久的任务,并将当前任务加入队列中。
* DiscardPolicy(丢弃策略):丢弃无法处理的任务。
线程池的示例
下面是一个简单的线程池示例,展示了如何使用线程池:
public class ThreadPoolExample {
public static void main(String[] args) {
// 创建线程池
ExecutorService executorService =
new ThreadPoolExecutor(2, // 核心线程数为2
5, // 最大线程数为5
100, // 超时时间为100毫秒
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(10), // 缓冲队列的大小为10
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()); // 线程池满了后,抛出异常
// 执行10个任务
for (int i = 0; i < 10; i++) {
executorService.execute(new Task(i));
}
// 关闭线程池
executorService.shutdown();
}
static class Task implements Runnable {
private int taskId;
public Task(int taskId) {
this.taskId = taskId;
}
@Override
public void run() {
System.out.println("Task " + taskId + " is running on " + Thread.currentThread().getName());
}
}
}
上述示例代码中,通过ThreadPoolExecutor类创建一个线程池executorService,并设置了线程池的核心线程数为2,最大线程数为5,超时时间为100毫秒,缓冲队列的大小为10,拒绝策略为线程池满了后,抛出异常。
然后,执行10个任务,每个任务都是一个Task对象,打印任务编号和运行的线程名。
自定义线程池
除了使用ThreadPoolExecutor提供的构造方法创建线程池外,也可以通过自定义方式来创建线程池,从而满足特定的业务需求,例如使用特定的拒绝策略或使用特定的队列。
下面是一个自定义线程池的示例:
public class CustomThreadPool {
public static void main(String[] args) {
// 创建自定义线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1, // corePoolSize
2, // maximumPoolSize
60, // keepAliveTime
TimeUnit.SECONDS, // 时间单位
new ArrayBlockingQueue<>(3), // 队列容量
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略
for (int i = 0; i < 5; i++) {
try {
// 添加任务到线程池
executor.execute(new Task(i));
} catch (Exception ex) {
ex.printStackTrace();
}
}
// 关闭线程池
executor.shutdown();
}
static class Task implements Runnable {
private int taskId;
public Task(int taskId) {
this.taskId = taskId;
}
@Override
public void run() {
try {
System.out.println("Task " + taskId + " is running on " + Thread.currentThread().getName());
Thread.sleep(3000);
System.out.println("Task " + taskId + " is completed.");
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}
上述示例代码中,通过ThreadPoolExecutor类创建线程池executor,并自定义了核心线程数为1,最大线程数为2,超时时间为60秒,队列容量为3,线程工厂使用默认,拒绝策略为CallerRunsPolicy,即调用提交任务的线程来处理该任务。
然后,通过for循环将5个任务添加到线程池executor中;每个任务都是一个Task对象,打印任务编号和运行的线程名。
最后,关闭线程池executor。由于自定义的线程池是异步的,因此可能会有一些任务尚未完成,但是当主线程结束时,所有任务也会被结束。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java并发线程之线程池的知识总结 - Python技术站