面试题答案
一键面试性能瓶颈
- 线程上下文切换开销:过多线程会导致频繁上下文切换,消耗CPU时间,降低执行效率。例如有大量短时间CPU密集型任务时,线程在执行、暂停、恢复过程中会浪费资源。
- 资源竞争:多个线程竞争CPU资源,可能导致某些线程长时间等待,使整体性能下降。比如多个线程同时访问共享数据且需要加锁同步时,会产生锁竞争。
优化方法
- 调整线程池参数
- 核心线程数:对于CPU密集型任务,核心线程数一般设置为CPU核心数 + 1。例如在4核CPU的机器上,核心线程数设为5,可充分利用CPU资源同时应对可能的线程阻塞。
- 最大线程数:通常与核心线程数一致,因为CPU密集型任务不需要额外大量线程。
- 队列容量:可以设置较小的队列容量,如5 - 10,避免任务在队列中长时间等待,使线程尽快执行任务。
- 线程优先级:可以根据任务重要性设置线程优先级。例如,对于关键计算任务,设置较高优先级,使CPU优先调度执行。但要注意,Java线程优先级是依赖于操作系统的,不同系统可能有不同表现。在实际中,优先处理重要任务计算的线程可设置为
Thread.MAX_PRIORITY
。 - 任务调度策略:使用合适的调度策略,如
Fork/Join
框架。它采用工作窃取算法,能有效利用多核CPU资源。比如在计算大型数组的总和时,可将任务分割成小任务,不同线程处理不同部分,空闲线程可窃取其他线程未完成的任务,提高整体效率。
实际案例
假设我们要计算一个超大矩阵的乘法,这是典型的CPU密集型任务。
import java.util.concurrent.*;
public class MatrixMultiplication {
private static final int CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors() + 1;
private static final int MAX_POOL_SIZE = CORE_POOL_SIZE;
private static final int QUEUE_CAPACITY = 5;
private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_POOL_SIZE,
0L,
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(QUEUE_CAPACITY)
);
public static void main(String[] args) {
int[][] matrixA = new int[1000][1000];
int[][] matrixB = new int[1000][1000];
// 初始化矩阵数据
Future<int[][]> future = executor.submit(() -> {
int[][] result = new int[matrixA.length][matrixB[0].length];
for (int i = 0; i < matrixA.length; i++) {
for (int j = 0; j < matrixB[0].length; j++) {
for (int k = 0; k < matrixB.length; k++) {
result[i][j] += matrixA[i][k] * matrixB[k][j];
}
}
}
return result;
});
try {
int[][] result = future.get();
// 处理结果
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
executor.shutdown();
}
}
}
在此案例中,通过合理设置线程池参数,能有效利用CPU资源进行矩阵乘法计算,提高性能。