面试题答案
一键面试1. 线程池的创建
- CPU密集型线程池:
解释:核心线程数和最大线程数设置为CPU核心数,因为CPU密集型任务主要靠CPU运算,过多线程反而会增加上下文切换开销。使用int cpuCore = Runtime.getRuntime().availableProcessors(); ThreadPoolExecutor cpuThreadPool = new ThreadPoolExecutor( cpuCore, cpuCore, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(100), new ThreadPoolExecutor.CallerRunsPolicy());
LinkedBlockingQueue
作为任务队列,CallerRunsPolicy
策略表示当队列满且线程池达到最大线程数时,新任务由提交任务的线程执行,这样能保证任务不会被丢弃。 - I/O密集型线程池:
解释:I/O密集型任务在等待I/O操作时线程会空闲,所以核心线程数可以设置为CPU核心数的2倍左右,最大线程数可以适当更大,这里设置为核心线程数的2倍。队列容量可根据实际情况调整,同样使用int ioCore = cpuCore * 2; ThreadPoolExecutor ioThreadPool = new ThreadPoolExecutor( ioCore, ioCore * 2, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(200), new ThreadPoolExecutor.CallerRunsPolicy());
CallerRunsPolicy
避免任务丢弃。
2. 任务提交策略
- CPU密集型任务:
直接将CPU密集型任务提交到CPU密集型线程池,确保任务在独立的资源环境中运行,避免对其他类型任务的干扰。cpuThreadPool.submit(() -> { // CPU密集型任务逻辑,例如复杂的计算 long result = 0; for (long i = 0; i < 1000000000L; i++) { result += i; } return result; });
- I/O密集型任务:
将I/O密集型任务提交到I/O密集型线程池,保证其有足够的资源处理I/O等待,同时不影响CPU密集型任务的执行。ioThreadPool.submit(() -> { // I/O密集型任务逻辑,例如文件读取或网络请求 try { FileReader reader = new FileReader("largeFile.txt"); int data; while ((data = reader.read()) != -1) { // 处理读取的数据 } reader.close(); } catch (IOException e) { e.printStackTrace(); } });
3. 资源监控与调整机制
- 资源监控:
- 使用
ThreadMXBean
监控CPU使用情况:ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); long cpuTimeBefore = threadMXBean.getCurrentThreadCpuTime(); // 执行任务 Future<Long> future = cpuThreadPool.submit(() -> { long result = 0; for (long i = 0; i < 1000000000L; i++) { result += i; } return result; }); try { long result = future.get(); long cpuTimeAfter = threadMXBean.getCurrentThreadCpuTime(); long cpuUsage = cpuTimeAfter - cpuTimeBefore; System.out.println("CPU usage: " + cpuUsage); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); }
- 使用
MBean
监控线程池状态:
这样可以通过JMX工具监控线程池的运行状态,如活跃线程数、任务队列大小等。ManagementFactory.getPlatformMBeanServer().registerMBean(cpuThreadPool, new ObjectName("ThreadPool:name=cpuThreadPool"));
- 使用
- 调整机制:
- 动态调整线程池大小:
解释:根据CPU使用率动态调整CPU密集型线程池的核心线程数和最大线程数,避免资源浪费或过度竞争。对于I/O密集型线程池也可类似根据I/O等待时间等指标进行调整。// 根据CPU使用率调整CPU密集型线程池大小 if (cpuUsage > 80) { if (cpuThreadPool.getCorePoolSize() < cpuCore * 2) { cpuThreadPool.setCorePoolSize(cpuThreadPool.getCorePoolSize() + 1); cpuThreadPool.setMaximumPoolSize(cpuThreadPool.getCorePoolSize() + 1); } } else if (cpuUsage < 20) { if (cpuThreadPool.getCorePoolSize() > cpuCore) { cpuThreadPool.setCorePoolSize(cpuThreadPool.getCorePoolSize() - 1); cpuThreadPool.setMaximumPoolSize(cpuThreadPool.getCorePoolSize() - 1); } }
- 动态调整线程池大小:
通过以上基于Java线程池的资源隔离方案,可以有效地将CPU密集型任务和I/O密集型任务进行资源隔离,同时通过资源监控和调整机制保证系统资源的高效利用。