Java线程池必知必会知识点总结
在并发编程中,线程池是一种重要的资源管理方式。线程池可以管理和执行多个线程,从而提高程序的性能和效率,同时还能避免线程创建和销毁的开销。
本文将介绍Java线程池的相关知识点,包括线程池的基本概念、实现原理、使用方法和注意事项。
线程池的基本概念
Java中的线程池主要有两种实现方式:FixedThreadPool和CachedThreadPool。
- FixedThreadPool指定池大小,当需要执行任务时如果池中没有可用线程,则任务会被阻塞,直到有足够的可用线程执行。
- CachedThreadPool不指定池大小,线程池会尽量使用空闲线程执行任务,如果没有空闲线程就会创建新线程执行任务,如果线程空闲时间超过指定时间(默认60秒)则被回收。
线程池的核心参数包括以下几个:
- corePoolSize: 线程池核心线程数,即线程池中最少的线程数。
- maximumPoolSize: 线程池最大线程数,即线程池中最多的线程数。
- keepAliveTime: 空闲线程存活时间,即超过corePoolSize的线程的最大空闲时间,单位为毫秒。
- TimeUnit: keepAliveTime的时间单位,可以是秒、分钟、小时等。
- BlockingQueue: 等待队列,用于存放尚未执行的任务。
线程池的实现原理
Java线程池的实现原理主要是通过ThreadPoolExecutor类实现的。
ThreadPoolExecutor是线程池的核心类,它负责管理线程的创建、销毁、调度以及任务的执行。ThreadPoolExecutor的初始化需要传入上述核心参数,以及一个RejectedExecutionHandler对象。
RejectedExecutionHandler是一个回调处理接口,当线程池无法执行新的任务时,会调用该接口的rejectedExecution方法。Java中提供了一些实现了该接口的类,比如AbortPolicy、CallerRunsPolicy、DiscardPolicy和DiscardOldestPolicy。如果不传入RejectedExecutionHandler对象,则使用默认的AbortPolicy。
线程池的使用方法
使用Java线程池主要包括以下步骤:
- 创建一个ThreadPoolExecutor对象,指定核心参数和RejectedExecutionHandler对象。
- 使用ThreadPoolExecutor的submit方法提交任务,submit()方法会返回Future对象,可以通过该对象判断任务是否执行完成。
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>());
for (int i = 0; i < 10; i++) {
executor.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is running");
}
});
}
上述代码创建了一个核心线程数为2,最大线程数为5的线程池,并往线程池中提交了10个任务。因为等待队列使用的是LinkedBlockingQueue,所以当线程池中有空闲线程时,会直接使用空闲线程执行任务。
- 关闭线程池。一般情况下,我们不需要手动调用ThreadPoolExecutor的shutdown方法关闭线程池,因为JVM会在程序退出时自动关闭线程池。但是如果需要手动关闭线程池,应该先调用ThreadPoolExecutor的shutdown方法,此时线程池会拒绝接受新任务,并尝试完成已经提交的任务。如果还有未完成的任务,这些任务会被等待完成,如果等待超时,则会强制中断还在运行的任务。
executor.shutdown();
线程池的注意事项
- 最大线程数的设置需要考虑当前环境的硬件资源,比如CPU核数、内存大小等。
- 使用线程池不能滥用,应该根据具体情况合理使用。
- 对于执行时间较短的任务,可以使用CachedThreadPool,对于执行时间长的任务,可以使用FixedThreadPool。
- 记得关闭线程池。
示例1:FixedThreadPool
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is running");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
executorService.shutdown();
上面的代码创建了一个核心线程数为5的线程池,并提交了10个任务。因为线程池的核心线程数只有5,所以只创建了5个线程执行任务,其他任务则等待空闲线程执行。
运行结果如下:
pool-1-thread-1 is running
pool-1-thread-2 is running
pool-1-thread-4 is running
pool-1-thread-3 is running
pool-1-thread-5 is running
pool-1-thread-1 is running
pool-1-thread-2 is running
pool-1-thread-4 is running
pool-1-thread-3 is running
pool-1-thread-5 is running
可以看到,线程池中的5个线程依次执行了10个任务,每个任务执行1秒钟。
示例2:CachedThreadPool
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is running");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
executorService.shutdown();
上面的代码创建了一个没有核心线程数限制的线程池,并提交了10个任务。因为线程池没有限制最大线程数,所以会根据任务的数量和执行时间来自动创建和回收线程。
运行结果如下:
pool-1-thread-2 is running
pool-1-thread-1 is running
pool-1-thread-3 is running
pool-1-thread-4 is running
pool-1-thread-5 is running
pool-1-thread-6 is running
pool-1-thread-7 is running
pool-1-thread-8 is running
pool-1-thread-10 is running
pool-1-thread-9 is running
可以看到,线程池中创建了10个线程,并且线程的创建和回收是自动的。每个任务执行1秒钟。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java线程池必知必会知识点总结 - Python技术站