手动创建线程池通过ThreadPoolExecutor配置核心参数,如corePoolSize、maximumPoolSize、workQueue等,实现灵活控制;而Executors工具类提供newFixedThreadPool、newCachedThreadPool等快捷方式,但可能因使用无界队列或无限线程数导致OOM。推荐手动创建以避免资源耗尽风险,并根据CPU核心数、任务类型(CPU或IO密集型)合理设置线程池大小,结合压力测试调整参数。关闭线程池时应先调用shutdown(),再通过awaitTermination()等待任务完成,必要时调用shutdownNow()强制终止,确保资源正确释放。

创建线程池的方式主要有手动创建和使用Executors工具类创建两种。手动创建可以更灵活地配置线程池参数,而Executors则提供了一些预定义的线程池,方便快捷。
手动创建和Executors工具类。
手动创建线程池的核心在于使用ThreadPoolExecutor类。你需要仔细配置它的几个关键参数:
LinkedBlockingQueue: 无界队列,可能导致OOM。ArrayBlockingQueue: 有界队列,可以防止OOM,但需要合理设置队列大小。SynchronousQueue: 不存储任务,直接提交给线程执行,如果线程池中没有空闲线程,则创建新线程,或者根据拒绝策略处理。AbortPolicy: 抛出RejectedExecutionException异常。CallerRunsPolicy: 由调用线程执行该任务。DiscardPolicy: 直接丢弃该任务。DiscardOldestPolicy: 丢弃队列中最老的任务,然后尝试执行当前任务。一个简单的例子:
import java.util.concurrent.*;
public class ThreadPoolExample {
public static void main(String[] args) {
int corePoolSize = 5;
int maximumPoolSize = 10;
long keepAliveTime = 60;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(100);
ThreadFactory threadFactory = Executors.defaultThreadFactory();
RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy();
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, rejectedExecutionHandler);
for (int i = 0; i < 10; i++) {
final int taskNumber = i;
executor.execute(() -> {
System.out.println("Task " + taskNumber + " executed by " + Thread.currentThread().getName());
try {
Thread.sleep(1000); // 模拟任务执行时间
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
executor.shutdown(); //不再接受新的任务
try {
executor.awaitTermination(10, TimeUnit.SECONDS); // 等待所有任务完成,最多等待10秒
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Finished all threads");
}
}Executors类提供了一些静态方法来创建预定义的线程池,简化了线程池的创建过程,但也隐藏了一些细节,需要谨慎使用:
LinkedBlockingQueue,队列长度无限制,可能导致OOM。SynchronousQueue,每个插入操作必须等待另一个线程的对应移除操作,所以线程数理论上可以无限增加,也可能导致OOM。LinkedBlockingQueue,队列长度无限制,可能导致OOM。使用示例:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecutorsExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
final int taskNumber = i;
executor.execute(() -> {
System.out.println("Task " + taskNumber + " executed by " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
executor.shutdown();
while (!executor.isTerminated()) {
// 等待所有任务完成
}
System.out.println("Finished all threads");
}
}虽然Executors简化了线程池的创建,但它隐藏了关键的配置细节,可能导致一些问题,特别是OOM。
newFixedThreadPool和newSingleThreadExecutor使用了无界的LinkedBlockingQueue,如果任务提交速度超过处理速度,队列会无限增长,最终导致OOM。newCachedThreadPool使用了SynchronousQueue,允许创建无限数量的线程,如果任务提交速度过快,可能会创建大量的线程,耗尽系统资源,导致OOM或系统崩溃。手动创建线程池可以让你更精确地控制线程池的参数,例如选择合适的队列类型和大小,设置合理的线程数量和keepAliveTime,从而避免这些潜在的问题。
线程池大小的选择是一个需要权衡的问题,没有一个固定的最佳值。通常需要考虑以下因素:
可以使用一些工具来监控线程池的性能,例如JConsole、VisualVM等。通过监控线程池的活跃线程数、队列长度、任务执行时间等指标,可以更好地了解线程池的运行状况,并根据实际情况进行调整。
一般来说,可以通过以下步骤来选择合适的线程池大小:
选择合适的线程池大小是一个迭代的过程,需要不断地监控和调整。
正确地关闭线程池非常重要,可以避免资源泄漏和程序异常退出。ExecutorService提供了两个方法来关闭线程池:
一般来说,推荐使用shutdown()方法来关闭线程池,因为它允许正在执行的任务完成,可以避免数据丢失或状态不一致。但是,如果需要立即关闭线程池,可以使用shutdownNow()方法。
在调用shutdown()或shutdownNow()方法后,可以使用awaitTermination()方法来等待所有任务完成。awaitTermination()方法会阻塞当前线程,直到所有任务完成或者超时。
executor.shutdown(); // 拒绝接受新的任务
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { // 等待60秒
executor.shutdownNow(); // 如果超时,强制关闭
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
System.err.println("线程池未能终止");
}
}
} catch (InterruptedException ie) {
executor.shutdownNow(); // 发生中断,强制关闭
Thread.currentThread().interrupt();
}这段代码首先调用shutdown()方法来拒绝接受新的任务,然后调用awaitTermination()方法来等待所有任务完成,最多等待60秒。如果在60秒内所有任务都完成了,那么线程池就正常关闭了。如果超时,那么调用shutdownNow()方法来强制关闭线程池,并再次调用awaitTermination()方法来等待正在执行的任务停止。如果在第二次等待超时后,线程池仍然未能终止,那么就打印一条错误信息。
需要注意的是,即使调用了shutdownNow()方法,也可能有一些任务无法立即停止,例如正在进行IO操作的任务。
另外,如果任务在执行过程中抛出了异常,可能会导致线程池中的线程提前终止。为了避免这种情况,可以在任务的run()方法中使用try-catch块来捕获异常,并进行处理。
以上就是创建线程池有哪几种方式?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号