面试题答案
一键面试性能下降原因分析
- 线程资源竞争:大量
CompletableFuture
任务使用thenRun
方法,意味着会有大量后续操作在异步线程中执行,可能导致线程资源竞争,大量线程等待CPU时间片,从而降低系统性能。 - 线程上下文切换开销:高并发时频繁的线程上下文切换会消耗额外的系统资源,增加系统开销,降低任务执行效率。
- 缺乏资源管理:如果没有对
CompletableFuture
任务进行合理的资源管理,例如没有限制任务数量,可能会导致系统资源耗尽,从而性能下降。
优化方案
- 使用线程池:创建一个合适大小的线程池来执行
CompletableFuture
任务,避免线程无限制创建。例如,使用ThreadPoolExecutor
:
ExecutorService executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>()
);
在执行CompletableFuture
任务时,使用该线程池:
CompletableFuture.supplyAsync(() -> {
// 任务逻辑
return result;
}, executor).thenRun(() -> {
// 后续操作
});
- 调整任务数量:根据系统资源(如CPU核心数、内存等)合理调整并发执行的任务数量。可以通过信号量来限制任务数量:
Semaphore semaphore = new Semaphore(concurrencyLimit);
CompletableFuture.supplyAsync(() -> {
try {
semaphore.acquire();
// 任务逻辑
return result;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
} finally {
semaphore.release();
}
}, executor).thenRun(() -> {
// 后续操作
});
健壮的异常处理机制
- 使用exceptionally方法:在
CompletableFuture
任务链中使用exceptionally
方法捕获异常,并返回默认值或进行异常处理:
CompletableFuture.supplyAsync(() -> {
// 可能抛出异常的任务逻辑
if (condition) {
throw new RuntimeException("Task failed");
}
return result;
}, executor).thenRun(() -> {
// 后续操作
}).exceptionally(ex -> {
// 异常处理逻辑
System.err.println("Caught exception: " + ex.getMessage());
return null;
});
- 自定义异常类:定义自定义异常类,以便更好地区分不同类型的异常,并在异常处理中进行针对性处理:
class MyTaskException extends RuntimeException {
public MyTaskException(String message) {
super(message);
}
}
CompletableFuture.supplyAsync(() -> {
// 可能抛出自定义异常的任务逻辑
if (condition) {
throw new MyTaskException("Custom task failed");
}
return result;
}, executor).thenRun(() -> {
// 后续操作
}).exceptionally(ex -> {
if (ex instanceof MyTaskException) {
// 处理自定义异常
System.err.println("Caught custom exception: " + ex.getMessage());
} else {
// 处理其他异常
System.err.println("Caught other exception: " + ex.getMessage());
}
return null;
});
- 记录关键信息:在异常处理中记录关键信息,如任务执行的上下文、参数等,以便后续排查问题。可以使用日志框架(如Log4j、SLF4J):
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(MyClass.class);
CompletableFuture.supplyAsync(() -> {
// 任务逻辑
Object context = getContext();
try {
if (condition) {
throw new RuntimeException("Task failed");
}
return result;
} catch (Exception e) {
logger.error("Task failed with context: " + context, e);
throw e;
}
}, executor).thenRun(() -> {
// 后续操作
}).exceptionally(ex -> {
// 异常处理逻辑
logger.error("Exception in thenRun", ex);
return null;
});
通过线程池进一步优化任务执行效率
- 优化线程池参数:根据任务类型(CPU密集型、I/O密集型)合理调整线程池参数。
- CPU密集型任务:线程池大小一般设置为CPU核心数 + 1,以充分利用CPU资源,减少线程上下文切换。
- I/O密集型任务:线程池大小可以设置为CPU核心数 * 2,因为I/O操作等待时间长,需要更多线程来利用CPU资源。
- 使用不同线程池:根据任务优先级或类型,使用不同的线程池。例如,将高优先级任务和低优先级任务分别提交到不同的线程池,确保高优先级任务能够及时执行。
ExecutorService highPriorityExecutor = new ThreadPoolExecutor(
highCorePoolSize,
highMaximumPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>()
);
ExecutorService lowPriorityExecutor = new ThreadPoolExecutor(
lowCorePoolSize,
lowMaximumPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>()
);
CompletableFuture.supplyAsync(() -> {
// 高优先级任务逻辑
return result;
}, highPriorityExecutor).thenRun(() -> {
// 后续操作
});
CompletableFuture.supplyAsync(() -> {
// 低优先级任务逻辑
return result;
}, lowPriorityExecutor).thenRun(() -> {
// 后续操作
});
- 监控线程池状态:通过
ThreadPoolExecutor
提供的方法(如getActiveCount
、getQueue().size()
等)监控线程池的运行状态,根据监控结果动态调整线程池参数。例如,当队列中任务数量过多时,适当增加线程池大小。
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
while (true) {
int activeCount = executor.getActiveCount();
int queueSize = executor.getQueue().size();
if (queueSize > threshold) {
executor.setMaximumPoolSize(executor.getMaximumPoolSize() + 5);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}