两万字长文-最全的线程池详细介绍,是一篇全面深入的Java线程池探讨,详解Executors工具与ThreadPoolExecutor的常用方法,示范实战案例,并剖析ThreadPoolExecutor的各种参数与最佳实践。从基础到高级,为读者提供了丰富的线程池知识,既是API工具,也是面试宝典。无论您是初学者还是专业开发者,都将在这篇长文中找到深度解析与实用技巧,助您在多线程编程领域游刃有余。
一、 无界线程池 newCachedThreadPool
Java中Executor框架提供的一个工厂方法,用于创建一个根据需要创建新线程的线程池。这种线程池在执行任务之前会尝试重用现有的空闲线程,如果没有可用的线程,则会创建新的线程。如果线程在60秒内没有被使用,它将被终止并从缓存中移除
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CachedThreadPoolDemo {
public static void main(String[] args) {
// 创建一个根据需要创建新线程的缓存线程池
ExecutorService executorService = Executors.newCachedThreadPool();
// 提交一些任务给线程池
for (int i = 1; i <= 5; i++) {
final int taskId = i;
executorService.submit(() -> {
System.out.println("Task " + taskId + " is executing by " + Thread.currentThread().getName());
try {
Thread.sleep(2000); // 模拟任务执行
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// 关闭线程池
executorService.shutdown();
}
}
创建了一个根据需要创建新线程的缓存线程池。然后,我们提交了5个任务给线程池。由于线程池的特性是根据需要动态创建线程,所以这里可能会创建新的线程来执行任务。如果线程在60秒内没有被使用,它将被终止并从缓存中移除。
缓存线程池适用于处理大量的短生命周期任务,因为它可以根据需要动态地调整线程池的大小,避免了创建过多线程导致资源浪费的问题。
1.1 无界线程池的创建过程是可定制的
如果你想要自定义线程的创建方式,你可以使用
newCachedThreadPool
方法的重载版本,该版本接受一个ThreadFactory
参数,允许你提供自定义的线程工厂
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
public class CachedThreadPoolWithCustomThreadFactory {
public static void main(String[] args) {
// 创建自定义线程工厂
ThreadFactory threadFactory = new ThreadFactory() {
@Override
public Thread newThread(Runnable runnable) {
Thread thread = new Thread(runnable);
thread.setName("CustomThread-" + thread.getId());
thread.setPriority(Thread.NORM_PRIORITY);
thread.setDaemon(false);
return thread;
}
};
// 创建一个根据需要创建新线程的缓存线程池,使用自定义线程工厂
var executorService = Executors.newCachedThreadPool(threadFactory);
// 提交一些任务给线程池
for (int i = 1; i <= 5; i++) {
final int taskId = i;
executorService.submit(() -> {
System.out.println("Task " + taskId + " is executing by " + Thread.currentThread().getName());
try {
Thread.sleep(2000); // 模拟任务执行
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// 关闭线程池
executorService.shutdown();
}
}
通过提供自定义的 ThreadFactory
来实现线程的定制。在 ThreadFactory
的实现中,我们设置了线程的名称、优先级和是否为守护线程。然后,我们使用自定义的线程工厂创建了一个根据需要创建新线程的缓存线程池。这样就能够按照自己的需求创建线程
1.2 无界线程池的优缺点
newCachedThreadPool
创建的线程池是一个无界线程池,它具有以下特点:
1.2.1 优点:
-
动态调整线程数量: 无界线程池会根据需求动态地创建新线程,无上限地适应任务的数量。当有新任务提交时,如果池中没有空闲线程,则会创建一个新线程来处理任务。这使得线程池能够灵活地适应工作负载的变化。
-
任务处理速度快: 由于可以根据需要创建新线程,无界线程池在瞬时负载较高的情况下能够更快地响应任务。
1.2.2 缺点:
-
可能导致资源耗尽: 由于线程数量没有上限,当有大量任务提交时,可能会创建大量线程,导致系统资源(如内存)耗尽。
-
可能导致过度竞争: 在高并发情况下,大量线程的创建可能导致线程之间的竞争,从而影响性能。
-
可能导致任务堆积: 如果任务的执行时间较长,而新任务不断提交,可能导致线程池中积累大量未完成的任务,影响系统的稳定性。
-
不适用于长期运行的任务: 对于长期运行的任务,无界线程池可能会导致创建大量的线程,而这些线程在任务完成后不会被回收,最终可能耗尽系统资源。
无界线程池适用于任务短暂、处理速度快的场景,但在长时间运行的任务或者负载较高的情况下,可能需要考虑其他类型的线程池,例如有界线程池或者使用任务队列进行任务排队。在选择线程池类型时,需要根据具体应用场景和系统资源来进行权衡。
二、有界线程池 newFixedThreadPool
Java中Executor框架提供的一个工厂方法**,用于创建固定大小的线程池**。这种线程池在应用程序的整个生命周期内都保持相同数量的线程,当有新的任务提交时,如果线程池中的线程数未达到最大值,将会创建新的线程来处理任务,否则任务将会被放入队列等待执行。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FixedThreadPoolDemo {
public static void main(String[] args) {
// 创建一个固定大小为3的线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
// 提交一些任务给线程池
for (int i = 1; i <= 5; i++) {
final int taskId = i;
executorService.submit(() -> {
System.out.println("Task " + taskId + " is executing by " + Thread.currentThread().getName());
try {
Thread.sleep(2000); // 模拟任务执行
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// 关闭线程池
executorService.shutdown();
}
}
创建了一个固定大小为3的线程池。然后,我们提交了5个任务给线程池。由于线程池的大小是3,因此最多只能同时执行3个任务,而其余的任务将会被放入队列等待执行。线程池会在有空闲线程时从队列中取出任务执行。
2.1 优缺点
2.1.1 优点:
- 控制资源使用: 有界线程池限制了线程的数量,防止线程数量无限增长,从而有效控制了系统资源的使用。
- 避免资源耗尽: 由于线程数量是有限的,不会无限制地创建新线程。这有助于避免系统资源(如内存)被大量线程耗尽。
- 稳定性: 有界线程池可以更好地保持系统的稳定性,避免过度竞争和任务堆积。
2.1.2 缺点:
- 灵活性差: 有界线程池的线程数量是固定的,不能动态调整。如果负载较大,可能导致线程不足;如果负载较小,可能会浪费资源。
- 不适用于瞬时高并发: 在某些瞬时高并发的场景下,有界线程池可能无法及时响应大量的任务,导致一些任务需要等待执行。
- 可能导致线程饥饿: 如果设置的线程数较小,并且任务提交速度较快,可能导致部分任务一直等待执行,产生线程饥饿的情况。
有界线程池适用于**相对稳定的工作负载,能够限制线程数量,防止资源耗尽。**在选择线程池类型时,需要根据应用场景和性能需求来进行权衡。
三、 newSingleThreadExecutor
用于创建一个包含单个线程的线程池。这个线程池确保所有提交的任务按照顺序执行,即每次只有一个线程在执行任务。如果这个线程因为异常而结束,会有另一个线程取代它,保持线程池中始终存在一个活动线程。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SingleThreadExecutorDemo {
public static void main(String[] args) {
// 创建一个包含单个线程的线程池
ExecutorService executorService =