面试题答案
一键面试使用Java Lambda表达式向线程池中提交任务并优化线程池性能
-
合理设置线程池参数:
- 核心线程数(corePoolSize):应根据任务的性质来设置。如果任务是CPU密集型,核心线程数一般设置为
Runtime.getRuntime().availableProcessors()
,这样能充分利用CPU资源。例如:
int corePoolSize = Runtime.getRuntime().availableProcessors(); ExecutorService executorService = new ThreadPoolExecutor( corePoolSize, corePoolSize * 2, 10L, TimeUnit.SECONDS, new LinkedBlockingQueue<>() );
- 最大线程数(maximumPoolSize):对于CPU密集型任务,一般可以设置为核心线程数的2倍左右。如果任务是I/O密集型,核心线程数可以设置为
Runtime.getRuntime().availableProcessors() * 2
,最大线程数可根据系统资源和预估的并发任务数适当增加。 - 存活时间(keepAliveTime):设置线程在没有任务时保持活动的时间。对于任务提交比较频繁的场景,可以设置较短的存活时间,如10秒,以节省资源。
- 任务队列(workQueue):如果任务执行时间较短且数量较多,可以选择
SynchronousQueue
,它不会存储任务,直接将任务交给线程处理,能减少排队延迟。如果任务执行时间较长,可以选择LinkedBlockingQueue
,它能存储大量任务,防止任务过多导致线程创建过多。
- 核心线程数(corePoolSize):应根据任务的性质来设置。如果任务是CPU密集型,核心线程数一般设置为
-
优化任务调度策略:
- 优先队列(PriorityQueue):如果任务有优先级之分,可以自定义任务类实现
Comparable
接口,然后使用PriorityQueue
作为任务队列。例如:
class PriorityTask implements Comparable<PriorityTask> { private int priority; // 其他任务相关属性和方法 @Override public int compareTo(PriorityTask other) { return Integer.compare(this.priority, other.priority); } } ExecutorService executorService = new ThreadPoolExecutor( corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, new PriorityQueue<>() );
- 使用
ForkJoinPool
:对于可以进行分治的任务,ForkJoinPool
能更有效地利用多核CPU资源。例如计算斐波那契数列的并行计算:
class FibonacciTask extends RecursiveTask<Integer> { final int n; FibonacciTask(int n) { this.n = n; } @Override protected Integer compute() { if (n <= 1) return n; FibonacciTask f1 = new FibonacciTask(n - 1); f1.fork(); FibonacciTask f2 = new FibonacciTask(n - 2); return f2.compute() + f1.join(); } } ForkJoinPool forkJoinPool = new ForkJoinPool(); int result = forkJoinPool.invoke(new FibonacciTask(10));
- 优先队列(PriorityQueue):如果任务有优先级之分,可以自定义任务类实现
-
考量因素:
- 任务类型:区分CPU密集型和I/O密集型任务,以便合理设置线程池参数。
- 系统资源:避免线程数过多导致系统资源耗尽,如内存不足。
- 任务优先级:如果有优先级要求,需要选择合适的任务调度策略。
- 吞吐量和延迟:提高吞吐量可能需要增加线程数,但过多线程会增加上下文切换开销,导致延迟增加,需要平衡两者关系。
-
Lambda表达式在与线程池结合使用时对代码结构和性能的影响:
- 代码结构:Lambda表达式使代码更简洁、易读。例如提交任务时:
executorService.submit(() -> { // 任务代码 System.out.println("Task executed in thread: " + Thread.currentThread().getName()); });
相比于传统的
Runnable
实现类,Lambda表达式减少了样板代码。- 性能:Lambda表达式本身对性能影响不大,但它使得代码更紧凑,在一定程度上有助于提高代码的可读性和维护性,从而间接对性能产生积极影响。同时,Lambda表达式在创建
Callable
或Runnable
实例时更高效,减少了类的创建开销。