面试题答案
一键面试减少线程开销
- 复用线程池:避免每次创建新线程,通过
Executors
创建线程池,如FixedThreadPool
、CachedThreadPool
等。例如:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CompletableFutureOptimization {
private static final ExecutorService executorService = Executors.newFixedThreadPool(10);
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Result";
}, executorService)
.thenApply(result -> {
System.out.println("Processed result: " + result);
return result.toUpperCase();
})
.exceptionally(ex -> {
System.out.println("Caught exception: " + ex.getMessage());
return "Default";
})
.thenAccept(System.out::println);
executorService.shutdown();
}
}
这里使用newFixedThreadPool(10)
创建了一个固定大小为10的线程池,supplyAsync
方法将任务提交到该线程池执行,减少了线程创建和销毁的开销。
- 减少不必要的异步操作:如果某些操作可以在主线程或当前线程执行而不影响整体并发性能,尽量同步执行。例如,一些简单的计算或数据转换操作可以在
thenApply
等回调方法中直接执行,而不是开启新的异步任务。
合理设置线程池
-
根据任务类型调整线程池大小:
- CPU密集型任务:线程池大小一般设置为
CPU核心数 + 1
,因为CPU密集型任务主要消耗CPU资源,过多线程会导致线程上下文切换开销增大。例如,在多核CPU系统中,若有8个核心,线程池大小设置为9。 - I/O密集型任务:线程池大小可以设置得较大,因为I/O操作会使线程处于等待状态,CPU有空闲时间。一般可以设置为
CPU核心数 * 2
,甚至更大,具体根据I/O等待时间与CPU计算时间的比例来调整。
- CPU密集型任务:线程池大小一般设置为
-
使用自定义线程池:通过
ThreadPoolExecutor
类自定义线程池参数,如corePoolSize
(核心线程数)、maximumPoolSize
(最大线程数)、keepAliveTime
(线程存活时间)等,以满足特定业务需求。例如:
import java.util.concurrent.*;
public class CustomThreadPoolExample {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, // corePoolSize
10, // maximumPoolSize
10L, TimeUnit.SECONDS, // keepAliveTime
new LinkedBlockingQueue<>(100), // workQueue
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy()
);
CompletableFuture.supplyAsync(() -> {
// 模拟任务
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Result";
}, executor)
.thenApply(result -> {
System.out.println("Processed result: " + result);
return result.toUpperCase();
})
.exceptionally(ex -> {
System.out.println("Caught exception: " + ex.getMessage());
return "Default";
})
.thenAccept(System.out::println);
executor.shutdown();
}
}
这里自定义了一个线程池,核心线程数为5,最大线程数为10,线程存活时间为10秒,任务队列大小为100,并设置了饱和策略为CallerRunsPolicy
,当任务队列满且线程数达到最大线程数时,新任务由提交任务的线程执行。
其他优化
- 批量处理任务:使用
CompletableFuture.allOf
或CompletableFuture.anyOf
方法批量处理多个任务。例如,当需要等待多个异步任务都完成时,使用allOf
:
import java.util.concurrent.CompletableFuture;
public class BatchTaskExample {
public static void main(String[] args) {
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Result1";
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Result2";
});
CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2);
allFutures.thenRun(() -> {
try {
System.out.println("Future1 result: " + future1.get());
System.out.println("Future2 result: " + future2.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}).exceptionally(ex -> {
System.out.println("Caught exception: " + ex.getMessage());
return null;
});
}
}
这样可以减少线程创建次数,提高整体性能。
- 优化异常处理:在
exceptionally
方法中尽量减少复杂操作,避免引入新的性能瓶颈。如果可能,将异常处理逻辑统一封装,避免在每个CompletableFuture
的exceptionally
中重复编写相同的处理代码。