面试官:线程池、自定义线程池、自定义拒绝策略有了解过吗?什么场景用?

hello啊,各位观众姥爷们!!!本baby今天又来报道了!哈哈哈哈哈嗝🐶

面试资料大全|各种技术资料-2000G

一、线程池核心原理

1. Java线程池体系结构

Executor (接口)ExecutorService (接口)AbstractExecutorService (抽象类)ThreadPoolExecutor (核心实现类)

2. ThreadPoolExecutor关键参数

public ThreadPoolExecutor(
    int corePoolSize,     // 核心线程数
    int maximumPoolSize,  // 最大线程数
    long keepAliveTime,  // 空闲线程存活时间
    TimeUnit unit,       // 时间单位
    BlockingQueue<Runnable> workQueue, // 工作队列
    ThreadFactory threadFactory,       // 线程工厂
    RejectedExecutionHandler handler   // 拒绝策略
)

3. 线程池工作流程

提交任务
核心线程是否已满?
创建核心线程执行
工作队列是否已满?
任务入队等待
线程数是否达到最大值?
创建非核心线程执行
执行拒绝策略

二、自定义线程池实践

1. 典型配置方案

// IO密集型任务配置
ThreadPoolExecutor ioIntensivePool = new ThreadPoolExecutor(
    2 * Runtime.getRuntime().availableProcessors(),
    4 * Runtime.getRuntime().availableProcessors(),
    60, TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(1000),
    new CustomThreadFactory("io-pool"),
    new CustomRejectionPolicy()
);

// CPU密集型任务配置
ThreadPoolExecutor cpuIntensivePool = new ThreadPoolExecutor(
    Runtime.getRuntime().availableProcessors(),
    Runtime.getRuntime().availableProcessors(),
    0, TimeUnit.SECONDS,
    new SynchronousQueue<>(),
    new CustomThreadFactory("cpu-pool")
);

2. 自定义线程工厂

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

    public CustomThreadFactory(String poolName) {
        this.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);
        return t;
    }
}

三、拒绝策略深度解析

1. JDK内置策略对比

策略类行为适用场景
AbortPolicy直接抛出RejectedExecutionException需要严格保证任务不丢失的场景
CallerRunsPolicy由提交任务的线程直接执行适合能接受降级的业务场景
DiscardPolicy静默丢弃任务监控不敏感的非关键任务
DiscardOldestPolicy丢弃队列中最老的任务并重试允许丢弃旧任务的实时性要求不高的场景

2. 自定义拒绝策略实现

public class CustomRejectionPolicy implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        // 1. 记录日志和监控指标
        log.warn("Task rejected: {}", r.toString());
        monitor.recordRejection();
        
        // 2. 尝试将任务持久化到数据库
        if (!saveToDb(r)) {
            // 3. 备用处理方案
            if (!executor.isShutdown()) {
                r.run(); // 由当前线程直接执行
            }
        }
    }
}

3. 混合策略实现

public class HybridRejectionHandler implements RejectedExecutionHandler {
    private final RejectedExecutionHandler[] handlers;
    
    public HybridRejectionHandler(RejectedExecutionHandler... handlers) {
        this.handlers = handlers;
    }
    
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        for (RejectedExecutionHandler handler : handlers) {
            try {
                handler.rejectedExecution(r, executor);
                return;
            } catch (Exception e) {
                log.error("Rejection handler failed", e);
            }
        }
        throw new RejectedExecutionException("All handlers failed");
    }
}

四、应用场景分析

1. 线程池选型场景

场景类型推荐配置理由
Web请求处理core=200, max=400, queue=500, 拒绝策略=CallerRunsPolicy平衡吞吐量和响应时间,避免雪崩
批量数据处理core=CPU数, max=CPU数, queue=10000, 拒绝策略=AbortPolicy保证数据处理顺序,队列缓冲大批量任务
实时交易系统core=CPU数2, max=CPU数4, queue=0(SynchronousQueue), 策略=自定义持久化低延迟优先,拒绝时确保交易不丢失
定时任务调度使用ScheduledThreadPoolExecutor专为定时任务优化

2. 拒绝策略选型场景

业务特征推荐策略原因分析
支付订单处理自定义持久化策略资金交易必须保证不丢失
用户行为日志收集DiscardOldestPolicy可以容忍部分日志丢失,保证最新数据优先
API网关限流AbortPolicy快速失败让调用方知道系统繁忙
后台报表生成CallerRunsPolicy降级为同步执行保证任务完成

五、高级实践技巧

1. 线程池监控

// 定时采集指标
ScheduledExecutorService monitor = Executors.newSingleThreadScheduledExecutor();
monitor.scheduleAtFixedRate(() -> {
    log.info("Pool status: {}/{} active, queue size: {}, completed: {}",
        pool.getActiveCount(),
        pool.getPoolSize(),
        pool.getQueue().size(),
        pool.getCompletedTaskCount());
}, 1, 1, TimeUnit.SECONDS);

2. 动态调参

// 根据负载动态调整
if (queueSize > threshold) {
    executor.setCorePoolSize(newCoreSize);
    executor.setMaximumPoolSize(newMaxSize);
}

3. 上下文传递

// 使用ThreadLocal + 装饰器模式
public class ContextAwareRunnable implements Runnable {
    private final Runnable task;
    private final Map<String, Object> context;
    
    public ContextAwareRunnable(Runnable task) {
        this.task = task;
        this.context = ThreadLocalContext.getCurrentContext();
    }
    
    @Override
    public void run() {
        ThreadLocalContext.setContext(context);
        try {
            task.run();
        } finally {
            ThreadLocalContext.clear();
        }
    }
}

六、常见问题解决方案

1. 线程泄漏问题

  • 症状:线程数持续增长不释放
  • 排查
    // 获取所有工作线程
    ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
    long[] threadIds = threadBean.getAllThreadIds();
    for (long id : threadIds) {
        ThreadInfo info = threadBean.getThreadInfo(id);
        if (info.getThreadName().startsWith("custom-pool")) {
            System.out.println(info); // 打印堆栈
        }
    }
    
  • 解决:确保任务不会无限阻塞

2. 任务堆积问题

  • 优化方案
    • 使用有界队列 + 合理的拒绝策略
    • 引入背压机制(如RxJava的Flowable)

3. 死锁问题

  • 预防措施
    • 避免跨任务锁竞争
    • 使用ThreadPoolExecutor#getActiveCount()监控

七、性能优化建议

  1. 队列选择

    • ArrayBlockingQueue:固定大小,内存友好
    • LinkedBlockingQueue:无界/有界灵活,吞吐量高
    • SynchronousQueue:直接传递,零队列
  2. 线程数公式

    • CPU密集型N_cpu + 1
    • IO密集型N_cpu * (1 + WT/ST)
      • WT:等待时间
      • ST:计算时间
  3. 预热优化

    executor.prestartAllCoreThreads(); // 启动所有核心线程
    
面试资料大全|各种技术资料-2000G

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值