本文共 5061 字,大约阅读时间需要 16 分钟。
线程池作为Java中并发执行的核心工具之一,通过合理管理资源,显著提升了程序的效率和响应速度。本文将从线程池的设计目的、创建方式、构造参数、任务执行机制以及实际应用场景等方面,深入探讨ThreadPoolExecutor的工作原理和使用技巧。
线程池的核心设计初衷在于解决资源分配和任务执行的性能瓶颈。传统的单线程模型在处理多任务时,需要频繁创建和销毁线程,这种做法不仅效率低下,还可能导致资源耗尽。线程池通过将线程资源池化,实现了任务的“即时”执行和“即进即出”管理模式。
线程池的主要优势体现在以下几个方面:
ThreadPoolExecutor是线程池操作的核心接口,而 Executors框架提供了多种线程池实现方式,如FixedThreadPool、SingleThreadExecutor、CachedThreadPool等。虽然这些实现方式简化了线程池的使用,但直接使用ThreadPoolExecutor的方式更为灵活和安全。
以下是选择ThreadPoolExecutor的主要原因:
ThreadPoolExecutor的构造涉及多个关键参数,理解每个参数的含义和作用对线程池的正确使用至关重要。
核心线程数(corePoolSize)
线程最大数量(maximumPoolSize)
线程存活时间(keepAliveTime)
任务队列(workQueue)
线程工厂(threadFactory)
拒绝策略(handler)
ThreadPoolExecutor的执行过程可以分为以下几个阶段:
在线程池执行任务时,线程的状态会发生变化,从而形成线程图。通过图示可以清晰地观察任务执行的流程和线程之间的关系。
线程图示:
通过图示可以看到,线程池中的线程在接收任务后,会执行任务并完成后返回。线程的状态从“运行中”到“完成”,任务从队列中取出并逐一执行。execute和submit都是线程池执行任务的方法,但两者在功能上有明显差异:
void execute(Runnable r)<T> Future<T> submit(Runnable r)示例:
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 10, 10L, TimeUnit.SECONDS, new LinkedBlockingQueue(20));// 使用execute提交任务executor.execute(() -> { System.out.println("Hello, execute.");});// 使用submit获取执行结果Future future = executor.submit(() -> { System.out.println("Hello, submit."); return "Success";});System.out.println(future.get()); 线程池的拒绝策略决定了资源耗尽时任务的处理方式。默认策略是AbortPolicy,会抛出异常并终止任务执行。
常见策略:
示例:
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 3, 10, TimeUnit.SECONDS, new LinkedBlockingQueue(2), ThreadPoolExecutor.AbortPolicy());for (int i = 0; i < 6; i++) { executor.execute(() -> { System.out.println(Thread.currentThread().getName()); });}// 输出结果:// pool-1-thread-2// pool-1-thread-1// pool-1-thread-3// pool-1-thread-1// pool-1-thread-1// pool-1-thread-3// RejectedExecutionException: Task ... rejected from java.util.concurrent.ThreadPoolExecutor 为了满足特定场景需求,可以自定义拒绝策略。通过提供自定义的RejectedExecutionHandler,可以在资源耗尽时采取特定处理方式。
示例:
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 3, 10, TimeUnit.SECONDS, new LinkedBlockingQueue(2), new RejectedExecutionHandler() { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { System.out.println("这是自定义的拒绝策略"); }});for (int i = 0; i < 15; i++) { executor.execute(() -> { System.out.println(Thread.currentThread().getName()); });} 线程池可以通过扩展ThreadPoolExecutor类,添加自定义的执行前后处理逻辑,提升线程池的功能和性能。
示例:
public class ThreadPoolExtend { public static void main(String[] args) { MyThreadPoolExecutor executor = new MyThreadPoolExecutor(2, 4, 10, TimeUnit.SECONDS, new LinkedBlockingQueue()); for (int i = 0; i < 3; i++) { executor.execute(() -> { System.out.println(Thread.currentThread().getName()); }); } } static class MyThreadPoolExecutor extends ThreadPoolExecutor { private final ThreadLocal localTime = new ThreadLocal<>(); public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); } @Override protected void beforeExecute(Thread t, Runnable r) { Long sTime = System.nanoTime(); localTime.set(sTime); System.out.println(String.format("%s | before | time=%s", t.getName(), sTime)); super.beforeExecute(t, r); } @Override protected void afterExecute(Runnable r, Throwable t) { Long eTime = System.nanoTime(); Long totalTime = eTime - localTime.get(); System.out.println(String.format("%s | after | time=%s | 耗时:%s 毫秒", Thread.currentThread().getName(), eTime, (totalTime / 1000000.0))); super.afterExecute(r, t); } }} 通过以上内容,可以清晰地了解ThreadPoolExecutor的工作原理及其在实际应用中的操作方法。线程池作为Java中并发编程的核心工具,其正确使用能够显著提升程序的性能和稳定性。在实际开发中,合理配置线程池参数并选择适当的拒绝策略,是确保程序高效运行的关键。
转载地址:http://ahjmz.baihongyu.com/