MST

星途 面试题库

面试题:自定义Java线程池实现及其优化

请设计并实现一个自定义的Java线程池,要求能够灵活配置核心线程数、最大线程数、任务队列类型等参数。实现后,针对一个模拟的高负载计算任务场景(例如大量复杂的数学运算),分析并优化你所实现的线程池,以提高系统整体性能和资源利用率。阐述优化思路及具体的实现方法。
41.1万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

1. 自定义Java线程池设计与实现

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

public class CustomThreadPool {

    private final int corePoolSize;
    private final int maximumPoolSize;
    private final long keepAliveTime;
    private final TimeUnit unit;
    private final BlockingQueue<Runnable> workQueue;
    private final ThreadPoolWorker[] workers;
    private int poolSize;

    public CustomThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
                            int workQueueCapacity) {
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.keepAliveTime = keepAliveTime;
        this.unit = unit;
        this.workQueue = new LinkedBlockingQueue<>(workQueueCapacity);
        this.workers = new ThreadPoolWorker[maximumPoolSize];
        this.poolSize = 0;
    }

    public void execute(Runnable task) {
        if (poolSize < corePoolSize) {
            ThreadPoolWorker worker = new ThreadPoolWorker(task);
            workers[poolSize++] = worker;
            worker.start();
        } else if (!workQueue.offer(task)) {
            if (poolSize < maximumPoolSize) {
                ThreadPoolWorker worker = new ThreadPoolWorker(task);
                workers[poolSize++] = worker;
                worker.start();
            } else {
                // 饱和策略,这里简单丢弃任务
                System.out.println("Task rejected: " + task);
            }
        }
    }

    private class ThreadPoolWorker extends Thread {
        private Runnable task;

        public ThreadPoolWorker(Runnable task) {
            this.task = task;
        }

        @Override
        public void run() {
            while (task != null || (task = workQueue.poll(keepAliveTime, unit)) != null) {
                try {
                    task.run();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                task = null;
            }
        }
    }
}

2. 模拟高负载计算任务场景

public class HighLoadTask implements Runnable {
    @Override
    public void run() {
        // 模拟复杂数学运算
        double result = 0;
        for (int i = 0; i < 1000000; i++) {
            result += Math.sqrt(i);
        }
        System.out.println(Thread.currentThread().getName() + " finished task, result: " + result);
    }
}

3. 优化思路及具体实现方法

优化思路

  • 合理调整核心线程数和最大线程数:根据系统CPU核心数、内存等资源以及任务特性来确定合适的线程数量。对于CPU密集型任务,核心线程数可设置为CPU核心数;对于I/O密集型任务,可适当增加核心线程数。
  • 选择合适的任务队列:不同的任务队列有不同的特性,如LinkedBlockingQueue是无界队列,可能导致OOM;ArrayBlockingQueue是有界队列,需要合理设置容量。对于高负载场景,可考虑使用SynchronousQueue,它不存储任务,直接将任务交给线程处理,减少任务在队列中的等待时间。
  • 优化饱和策略:原实现简单丢弃任务,可改为将任务放入一个额外的队列,由一个专门的线程定期重试执行,或者使用ThreadPoolExecutor.CallerRunsPolicy,让提交任务的线程自己执行任务,以减轻线程池压力。
  • 线程复用与管理:合理设置keepAliveTime,使空闲线程在一段时间后销毁,避免过多空闲线程占用资源。同时,可实现一个线程池监控机制,动态调整线程池参数。

具体实现方法

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class OptimizedCustomThreadPool {

    private final int corePoolSize;
    private final int maximumPoolSize;
    private final long keepAliveTime;
    private final TimeUnit unit;
    private final BlockingQueue<Runnable> workQueue;
    private final ThreadPoolExecutor executor;

    public OptimizedCustomThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit) {
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.keepAliveTime = keepAliveTime;
        this.unit = unit;
        this.workQueue = new SynchronousQueue<>();
        this.executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
                new ThreadPoolExecutor.CallerRunsPolicy());
    }

    public void execute(Runnable task) {
        executor.execute(task);
    }

    public void shutdown() {
        executor.shutdown();
    }
}

在上述优化后的代码中:

  • 使用SynchronousQueue作为任务队列,减少任务在队列中的等待时间。
  • 使用ThreadPoolExecutor并设置CallerRunsPolicy饱和策略,让提交任务的线程执行任务,缓解线程池压力。
  • 可通过executor的其他方法,如shutdown优雅关闭线程池,以及实现监控机制来动态调整线程池参数。

通过这些优化,可以提高系统整体性能和资源利用率,更好地应对高负载计算任务场景。