Java线程池ThreadPoolExecutor使用与源码剖析

一、前言

在现代Java应用程序开发中,线程池已成为处理并发任务的标准模式。合理使用线程池能够显著提高系统性能,降低资源消耗,同时还能提供更好的线程管理能力。Java通过ThreadPoolExecutor类提供了强大而灵活的线程池实现,本文将深入探讨其使用方法和内部实现原理。

(因为很少用CSDN写文章,目录的分点没搞明白,有些题目的数字号有问题,还请见谅)

二、线程池概述

2.1 为什么需要线程池

在传统模式下,每当需要执行异步任务时,我们通常会直接创建一个新线程:

new Thread(new Runnable() {
    @Override
    public void run() {
        // 任务逻辑
    }
}).start();

这种方式存在几个明显问题:

  1. 线程创建和销毁开销大:每次创建线程都需要操作系统资源,线程销毁后资源又需要回收

  2. 资源耗尽风险:无限制创建线程可能导致系统资源耗尽

  3. 缺乏管理:难以对线程进行统一管理和监控

线程池通过预先创建一定数量的线程并重复利用它们,有效解决了上述问题。

2.2 Java中的线程池体系

Java的线程池实现位于java.util.concurrent包中,核心类继承关系如下:

Executor
├── ExecutorService
│   ├── AbstractExecutorService
│   │   └── ThreadPoolExecutor
│   └── ScheduledExecutorService
│       └── ScheduledThreadPoolExecutor

ThreadPoolExecutor是最核心的实现类,提供了完整的线程池功能。Executors工具类提供了一些常用的线程池工厂方法,但了解ThreadPoolExecutor的直接使用更为重要。

三、ThreadPoolExecutor核心参数

ThreadPoolExecutor的完整构造函数如下:

public ThreadPoolExecutor(int corePoolSize,
                         int maximumPoolSize,
                         long keepAliveTime,
                         TimeUnit unit,
                         BlockingQueue<Runnable> workQueue,
                         ThreadFactory threadFactory,
                         RejectedExecutionHandler handler)

下面我们详细解析每个参数的含义和作用。

3.1 corePoolSize(核心线程数)

corePoolSize指定了线程池中保持活动状态的最小线程数,即使它们处于空闲状态。除非设置了allowCoreThreadTimeOut,否则核心线程不会被回收。

特点

  • 线程池刚创建时,线程数为0,随着任务到来逐渐增加到corePoolSize

  • 当线程数达到corePoolSize后,新任务会被放入工作队列

  • 核心线程通常不会被回收,保持常驻状态

设置建议

  • 对于CPU密集型任务,通常设置为CPU核心数+1

  • 对于IO密集型任务,可以设置更大一些,具体取决于IO等待时间

3.2 maximumPoolSize(最大线程数)

maximumPoolSize指定了线程池中允许存在的最大线程数。当工作队列已满且当前线程数小于maximumPoolSize时,线程池会创建新线程处理任务。

特点

  • 当线程数超过corePoolSize但小于maximumPoolSize时,新创建的线程会在空闲keepAliveTime时间后被回收

  • 当线程数达到maximumPoolSize且队列已满时,会触发拒绝策略

设置建议

  • 需要根据系统资源和任务特性综合考虑

  • 通常设置为corePoolSize的1.5-2倍

  • 对于突发流量大的系统,可以设置更大一些

3.3 keepAliveTime(线程空闲时间)

keepAliveTime指定了非核心线程空闲时的存活时间。当线程空闲时间超过keepAliveTime时,线程会被回收,直到线程数降至corePoolSize。

特点

  • 仅对超过corePoolSize的线程有效

  • 如果设置了allowCoreThreadTimeOut(true),则核心线程也会受此参数控制

  • 单位由TimeUnit参数指定

设置建议

  • 根据任务到达的频繁程度设置

  • 对于突发任务较多的场景,可以设置较长一些

  • 默认值通常为60秒

3.4 unit(时间单位)

unit是keepAliveTime的时间单位,常用的有:

  • TimeUnit.NANOSECONDS

  • TimeUnit.MICROSECONDS

  • TimeUnit.MILLISECONDS

  • TimeUnit.SECONDS

  • TimeUnit.MINUTES

3.5 workQueue(工作队列)

workQueue是用于保存等待执行的任务的阻塞队列。线程池的工作队列选择对线程池行为有重大影响。

常用队列类型

  1. ArrayBlockingQueue:基于数组的有界阻塞队列

    • 特点:固定大小,公平性可选

    • 适用场景:需要控制队列大小的场景

  2. LinkedBlockingQueue:基于链表的阻塞队列

    • 特点:默认无界(Integer.MAX_VALUE),也可设置为有界

    • 适用场景:大多数场景,特别是任务到达速度波动大的情况

  3. SynchronousQueue:不存储元素的阻塞队列

    • 特点:每个插入操作必须等待一个移除操作

    • 适用场景:需要直接传递任务的场景,通常配合较大的maximumPoolSize

  4. PriorityBlockingQueue:具有优先级的无界阻塞队列

    • 特点:按优先级排序,无界

    • 适用场景:需要任务优先级的场景

队列选择策略

  • 对于短时突发任务:LinkedBlockingQueue或ArrayBlockingQueue

  • 对于需要快速响应的任务:SynchronousQueue

  • 对于需要优先级的任务:PriorityBlockingQueue

3.6 threadFactory(线程工厂)

threadFactory用于创建新线程。通过自定义线程工厂,可以设置线程的名称、优先级、守护状态等。

默认实现

static class DefaultThreadFactory implements ThreadFactory {
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;

    DefaultThreadFactory() {
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() :
                              Thread.currentThread().getThreadGroup();
        namePrefix = "pool-" +
                      poolNumber.getAndIncrement() +
                     "-thread-";
    }

    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r,
                              namePrefix + threadNumber.getAndIncrement(),
                              0);
        if (t.isDaemon())
            t.setDaemon(false);
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY);
        return t;
    }
}

自定义线程工厂示例

public class CustomThreadFactory implements ThreadFactory {
    private final String namePrefix;
    private final AtomicInteger threadNumber = new AtomicInteger(1);

    public CustomThreadFactory(String poolName) {
        namePrefix = poolName + "-thread-";
    }

    @Override
    public Thread newThread(Runnable r) {
        Thread t = new Thread(r, namePrefix + threadNumber.getAndIncrement());
        t.setDaemon(false);
        t.setPriority(Thread.NORM_PRIORITY);
        t.setUncaughtExceptionHandler((thread, throwable) -> 
            System.err.println("Uncaught exception in " + thread.getName() + ": " + throwable));
        return t;
    }
}

3.7 handler(拒绝策略)

handler是当线程池和工作队列都达到容量上限时,对新任务采取的处理策略。Java提供了四种内置策略:

  1. AbortPolicy(默认):直接抛出RejectedExecutionException

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        throw new RejectedExecutionException("Task " + r.toString() +
                                             " rejected from " +
                                             e.toString());
    }

  2. CallerRunsPolicy:由调用线程直接执行该任务

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            r.run();
        }
    }

  3. DiscardPolicy:静默丢弃被拒绝的任务

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }

  4. DiscardOldestPolicy:丢弃队列中最旧的任务,然后尝试重新提交当前任务

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            e.getQueue().poll();
            e.execute(r);
        }
    }
     

自定义拒绝策略示例

public class CustomRejectionPolicy implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        // 记录日志
        System.err.println("Task rejected: " + r.toString());
        
        // 保存到数据库或文件,稍后重试
        saveTaskForLaterExecution(r);
        
        // 发送告警
        sendAlert();
    }
    
    private void saveTaskForLaterExecution(Runnable r) {
        // 实现任务保存逻辑
    }
    
    private void sendAlert() {
        // 实现告警逻辑
    }
}

四、ThreadPoolExecutor执行流程

4.1 任务提交与执行流程

ThreadPoolExecutor的任务执行流程是其核心逻辑,下面我们通过源码分析这一过程。

入口方法execute()

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    
    int c = ctl.get();
    
    // 阶段1:当前线程数 < corePoolSize
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    
    // 阶段2:线程数 >= corePoolSize,尝试入队
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    
    // 阶段3:队列已满,尝试创建新线程
    else if (!addWorker(command, false))
        reject(command);
}

流程图

开始
↓
提交任务command
↓
检查command是否为null → 是 → 抛出NullPointerException
↓ 否
获取当前线程池状态ctl
↓
if (当前线程数 < corePoolSize) → 是 → 尝试创建新Worker → 成功 → 结束
↓ 否 or 创建失败
if (线程池运行中 && 任务入队成功) → 是 → 双重检查线程池状态
    ↓
    if (线程池不再运行 && 移除任务成功) → 是 → 执行拒绝策略
    ↓ 否
    if (当前线程数 == 0) → 是 → 创建新Worker(不带初始任务)
    ↓ 否
    结束
↓ 否 or 入队失败
if (尝试创建新Worker(非核心)) → 成功 → 结束
↓ 失败
执行拒绝策略
↓
结束

4.2 Worker内部类解析

WorkerThreadPoolExecutor的内部类,负责封装工作线程和执行任务:

private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
    final Thread thread;  // 实际执行任务的线程
    Runnable firstTask;   // 初始任务,可能为null
    volatile long completedTasks;  // 完成的任务数
    
    Worker(Runnable firstTask) {
        setState(-1); // 禁止中断直到runWorker
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);
    }
    
    public void run() {
        runWorker(this);
    }
    
    // 省略锁相关方法...
}

4.3 runWorker方法解析

runWorker是实际执行任务的循环:

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // 允许中断
    boolean completedAbruptly = true;
    try {
        while (task != null || (task = getTask()) != null) {
            w.lock();
            
            // 如果线程池正在停止,确保线程被中断
            if ((runStateAtLeast(ctl.get(), STOP) ||
                (Thread.interrupted() &&
                 runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            
            try {
                beforeExecute(wt, task);
                try {
                    task.run();
                    afterExecute(task, null);
                } catch (Throwable ex) {
                    afterExecute(task, ex);
                    throw ex;
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}

4.4 getTask方法解析

getTask方法负责从队列中获取任务:

private Runnable getTask() {
    boolean timedOut = false; // 上次poll是否超时
    
    for (;;) {
        int c = ctl.get();
        
        // 检查线程池状态
        if (runStateAtLeast(c, SHUTDOWN)
            && (runStateAtLeast(c, STOP) || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }
        
        int wc = workerCountOf(c);
        
        // 是否允许回收线程:允许核心线程超时或当前线程数超过核心数
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
        
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }
        
        try {
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

五、线程池状态管理

ThreadPoolExecutor使用一个AtomicInteger变量ctl来同时维护线程池状态和线程数量:

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

// 运行状态保存在高位
private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;

// 获取运行状态
private static int runStateOf(int c)     { return c & ~CAPACITY; }
// 获取工作线程数
private static int workerCountOf(int c)  { return c & CAPACITY; }
// 组合运行状态和工作线程数
private static int ctlOf(int rs, int wc) { return rs | wc; }

线程池有以下五种状态:

  1. RUNNING:接受新任务并处理队列中的任务

  2. SHUTDOWN:不接受新任务,但处理队列中的任务

  3. STOP:不接受新任务,不处理队列中的任务,并中断正在进行的任务

  4. TIDYING:所有任务已终止,workerCount为0,线程过渡到TIDYING状态将运行terminated()钩子方法

  5. TERMINATED:terminated()方法已完成

状态转换图:

RUNNING -> SHUTDOWN
   调用shutdown()
   
RUNNING/SHUTDOWN -> STOP
   调用shutdownNow()
   
STOP -> TIDYING
   当线程池和队列都为空时
   
SHUTDOWN -> TIDYING
   当队列和线程池都为空时
   
TIDYING -> TERMINATED
   当terminated()钩子方法完成时

六、线程池的异常处理

线程池的异常处理是一个容易被忽视但又非常重要的方面。ThreadPoolExecutor提供了不同的任务提交方式,它们的异常处理机制也有所不同。

6.1 execute()方法的异常处理

execute()方法提交的任务如果抛出未捕获异常,会导致执行该任务的线程终止:

executor.execute(() -> {
    throw new RuntimeException("Oops!");
});
  1. 通过ThreadFactory设置UncaughtExceptionHandler

ThreadFactory factory = r -> {
    Thread t = new Thread(r);
    t.setUncaughtExceptionHandler((thread, throwable) -> {
        System.err.println("Exception in thread " + thread.getName() + ": " + throwable);
    });
    return t;
};
 

2.在任务内部捕获所有异常:

executor.execute(() -> {
    try {
        // 任务代码
    } catch (Throwable t) {
        // 处理异常
    }
});

6.2 submit()方法的异常处理

submit()方法返回一个Future对象,异常会被封装在Future中,只有在调用Future.get()时才会抛出:

Future<?> future = executor.submit(() -> {
    throw new RuntimeException("Oops!");
});

try {
    future.get(); // 这里会抛出ExecutionException
} catch (ExecutionException e) {
    Throwable cause = e.getCause(); // 获取实际异常
    System.err.println("Task failed: " + cause);
}

submit()的三种形式

Future<?> submit(Runnable task)
<T> Future<T> submit(Runnable task, T result)
<T> Future<T> submit(Callable<T> task)

6.3 afterExecute钩子方法

可以继承ThreadPoolExecutor并重写afterExecute方法来处理任务执行后的异常:

public class CustomThreadPool extends ThreadPoolExecutor {
    // 构造方法省略...
    
    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        if (t == null && r instanceof Future<?>) {
            try {
                Future<?> future = (Future<?>) r;
                if (future.isDone())
                    future.get();
            } catch (CancellationException ce) {
                t = ce;
            } catch (ExecutionException ee) {
                t = ee.getCause();
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
            }
        }
        if (t != null) {
            System.err.println("Uncaught exception in thread pool: " + t);
            // 其他处理逻辑...
        }
    }
}

6.4 异常处理最佳实践

  1. 明确任务边界:每个任务应该处理自己的异常,不传播到线程池

  2. 使用Callable代替Runnable:Callable可以更好地处理异常和返回值

  3. 合理使用Future:对于需要结果的任务,使用submit()和Future

  4. 记录未捕获异常:通过UncaughtExceptionHandler记录所有未捕获异常

  5. 监控线程池健康状态:定期检查线程池的异常情况

七、线程池的监控与调优

7.1 监控线程池状态

ThreadPoolExecutor提供了一系列方法用于监控线程池状态:

// 获取核心线程数
int getCorePoolSize()

// 获取当前线程数
int getPoolSize()

// 获取活动线程数(正在执行任务的线程)
int getActiveCount()

// 获取最大允许线程数
int getMaximumPoolSize()

// 获取已完成任务数
long getCompletedTaskCount()

// 获取任务总数(已完成+正在执行+队列中等待)
long getTaskCount()

// 获取队列中的任务数
BlockingQueue<Runnable> getQueue()

自定义监控工具

public class ThreadPoolMonitor implements Runnable {
    private final ThreadPoolExecutor executor;
    private final int intervalSec;
    
    public ThreadPoolMonitor(ThreadPoolExecutor executor, int intervalSec) {
        this.executor = executor;
        this.intervalSec = intervalSec;
    }
    
    @Override
    public void run() {
        while (true) {
            monitor();
            try {
                TimeUnit.SECONDS.sleep(intervalSec);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
    
    private void monitor() {
        System.out.println(
            String.format("[monitor] [%d/%d] Active: %d, Completed: %d, Task: %d, Queue: %d/%d, isShutdown: %s, isTerminated: %s",
                executor.getPoolSize(),
                executor.getMaximumPoolSize(),
                executor.getActiveCount(),
                executor.getCompletedTaskCount(),
                executor.getTaskCount(),
                executor.getQueue().size(),
                executor.getQueue().remainingCapacity(),
                executor.isShutdown(),
                executor.isTerminated()));
    }
}

7.2 动态调整线程池参数

ThreadPoolExecutor允许运行时调整核心参数:

// 设置核心线程数
void setCorePoolSize(int corePoolSize)

// 设置最大线程数
void setMaximumPoolSize(int maximumPoolSize)

// 设置线程空闲时间
void setKeepAliveTime(long time, TimeUnit unit)

// 允许/禁止核心线程超时
void allowCoreThreadTimeOut(boolean value)

动态调整示例

public class DynamicThreadPoolAdjuster {
    private final ThreadPoolExecutor executor;
    
    public DynamicThreadPoolAdjuster(ThreadPoolExecutor executor) {
        this.executor = executor;
    }
    
    public void adjustBasedOnLoad() {
        int activeCount = executor.getActiveCount();
        int poolSize = executor.getPoolSize();
        int queueSize = executor.getQueue().size();
        
        // 如果活动线程数等于池大小且队列不为空,考虑扩容
        if (activeCount == poolSize && queueSize > 0) {
            int newPoolSize = Math.min(
                poolSize * 2,
                executor.getMaximumPoolSize());
            executor.setCorePoolSize(newPoolSize);
        }
        
        // 如果活动线程数远小于池大小,考虑缩容
        else if (activeCount < poolSize / 2) {
            int newPoolSize = Math.max(
                poolSize / 2,
                executor.getCorePoolSize());
            executor.setCorePoolSize(newPoolSize);
        }
    }
}

7.3 线程池调优建议

  1. CPU密集型任务

    • corePoolSize = CPU核心数 + 1

    • maximumPoolSize = corePoolSize或稍大

    • 使用有界队列防止资源耗尽

  2. IO密集型任务

    • corePoolSize可以设置较大(如CPU核心数*2)

    • maximumPoolSize可以设置更大(如CPU核心数*4)

    • 考虑使用SynchronousQueue或较小的有界队列

  3. 混合型任务

    • 将任务分类,使用不同的线程池

    • 或者根据IO等待时间比例调整线程数

  4. 通用建议

    • 避免无界队列,防止内存溢出

    • 合理设置线程存活时间

    • 为线程池命名,方便监控和排查问题

    • 考虑使用自定义拒绝策略记录被拒绝的任务

八、线程池的常见陷阱与最佳实践

8.1 常见陷阱

  1. 无界队列导致OOM

    // 错误示例:使用无界队列且没有合理的拒绝策略
    new ThreadPoolExecutor(
        10, 10,
        0L, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<>());  // 默认无界
     
  2. 不合理的线程数设置

    • 核心线程数过大:资源浪费

    • 最大线程数过小:无法应对突发流量

    • 核心线程数过小:队列堆积

  3. 忽略任务异常

    executor.submit(() -> {
        throw new RuntimeException("This will be ignored!");
    });
     
  4. 线程上下文切换开销

    • 线程数过多会导致大量CPU时间花在线程切换上

  5. 死锁风险

    • 线程池中的任务相互等待可能导致死锁

  6. 资源泄漏

    • 忘记关闭线程池会导致资源无法释放

8.2 最佳实践

  1. 合理配置线程池参数

    int corePoolSize = Runtime.getRuntime().availableProcessors() + 1;
    int maxPoolSize = corePoolSize * 2;
    ThreadPoolExecutor executor = new ThreadPoolExecutor(
        corePoolSize, maxPoolSize,
        60L, TimeUnit.SECONDS,
        new ArrayBlockingQueue<>(100),
        new CustomThreadFactory("app-thread-pool"),
        new CustomRejectionPolicy());
     
  2. 使用有界队列

    new ArrayBlockingQueue<>(1000)  // 根据系统负载设置合理大小
     
  3. 合理处理异常

    • 使用Future.get()检查异常

    • 重写afterExecute方法

    • 设置UncaughtExceptionHandler

  4. 正确关闭线程池

    executor.shutdown();  // 温和关闭
    try {
        if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
            executor.shutdownNow();  // 强制关闭
        }
    } catch (InterruptedException e) {
        executor.shutdownNow();
        Thread.currentThread().interrupt();
    }
     
  5. 避免在任务中使用ThreadLocal

    • 线程池中的线程会重用,可能导致ThreadLocal污染

  6. 为不同的任务类型使用不同的线程池

    • 将CPU密集型、IO密集型和关键任务分开处理

  7. 监控和动态调整

           

    • 定期监控线程池状态

    • 根据负载动态调整参数

      九、高级主题与扩展

      9.1 ForkJoinPool与Work-Stealing算法

      Java 7引入了ForkJoinPool,它使用工作窃取(work-stealing)算法,适合处理可以递归分解的任务。

      ThreadPoolExecutor的主要区别:

    • 每个线程有自己的工作队列

    • 当线程自己的队列为空时,可以从其他线程队列"窃取"任务

    • 更适合处理大量小任务或可以递归分解的任务

    • 参数配置:理解corePoolSize、maximumPoolSize、workQueue等参数的含义和相互关系

    • 执行流程:掌握任务提交、线程创建、队列处理、拒绝策略的完整流程

    • 异常处理:区分execute()和submit()的异常处理机制,合理捕获和处理异常

    • 监控调优:建立线程池监控机制,根据实际负载动态调整参数

    • 最佳实践:避免常见陷阱,遵循线程池使用的最佳实践

    • 通过深入理解ThreadPoolExecutor的内部机制,开发者可以构建更健壮、高效的并发应用程序。

      9.2 CompletableFuture与异步编程

      Java 8引入的CompletableFuture提供了更强大的异步编程能力,内部也使用线程池:

      CompletableFuture.supplyAsync(() -> {
          // 异步任务
          return doSomething();
      }, executor)  // 可以指定自定义线程池
      .thenApply(result -> {
          // 处理结果
          return process(result);
      })
      .exceptionally(ex -> {
          // 异常处理
          return handleException(ex);
      });
       

      9.3 定时任务执行

      ScheduledThreadPoolExecutorThreadPoolExecutor的子类,支持定时和周期性任务:

      ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(4);
      
      // 延迟执行
      scheduler.schedule(() -> {
          System.out.println("Delayed task");
      }, 1, TimeUnit.SECONDS);
      
      // 周期性执行
      scheduler.scheduleAtFixedRate(() -> {
          System.out.println("Periodic task");
      }, 0, 1, TimeUnit.SECONDS);
       

      9.4 自定义线程池扩展

      可以通过继承ThreadPoolExecutor并重写其钩子方法来实现自定义行为:

      public class PausableThreadPool extends ThreadPoolExecutor {
          private boolean isPaused;
          private final ReentrantLock pauseLock = new ReentrantLock();
          private final Condition unpaused = pauseLock.newCondition();
      
          public PausableThreadPool(int corePoolSize, int maximumPoolSize, 
                                  long keepAliveTime, TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue) {
              super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
          }
      
          @Override
          protected void beforeExecute(Thread t, Runnable r) {
              super.beforeExecute(t, r);
              pauseLock.lock();
              try {
                  while (isPaused) unpaused.await();
              } catch (InterruptedException ie) {
                  t.interrupt();
              } finally {
                  pauseLock.unlock();
              }
          }
      
          public void pause() {
              pauseLock.lock();
              try {
                  isPaused = true;
              } finally {
                  pauseLock.unlock();
              }
          }
      
          public void resume() {
              pauseLock.lock();
              try {
                  isPaused = false;
                  unpaused.signalAll();
              } finally {
                  pauseLock.unlock();
              }
          }
      }
       

      十、总结

      ThreadPoolExecutor是Java并发编程中的核心组件,合理使用线程池可以显著提高系统性能。本文详细剖析了其核心参数、执行流程、异常处理机制以及常见陷阱和最佳实践。关键点总结:

    • 参数配置:理解corePoolSize、maximumPoolSize、workQueue等参数的含义和相互关系

    • 执行流程:掌握任务提交、线程创建、队列处理、拒绝策略的完整流程

    • 通过深入理解ThreadPoolExecutor的内部机制,开发者可以构建更健壮、高效的并发应用程序。

    • 异常处理:区分execute()和submit()的异常处理机制,合理捕获和处理异常

    • 监控调优:建立线程池监控机制,根据实际负载动态调整参数

    • 最佳实践:避免常见陷阱,遵循线程池使用的最佳实践

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值