面试题答案
一键面试一、Executors创建线程池的问题
- FixedThreadPool和SingleThreadPool:使用
LinkedBlockingQueue
作为工作队列,该队列容量默认是Integer.MAX_VALUE
,可能导致大量任务堆积,从而耗尽内存。 - CachedThreadPool:使用
SynchronousQueue
作为工作队列,该队列没有容量,会导致线程数不断增加,可能耗尽系统资源。 - ScheduledThreadPool:虽然相对较为安全,但如果任务执行时间过长,可能影响后续任务的调度。
二、改进方案 - 使用ThreadPoolExecutor合理配置参数
- 核心线程数(corePoolSize):根据应用场景预估长期维持的活跃线程数。例如,对于I/O密集型任务,核心线程数可以设置为CPU核心数的2 - 3倍,因为I/O操作时线程会有大量时间处于等待状态,需要更多线程来利用CPU资源;对于CPU密集型任务,核心线程数可以设置为CPU核心数,避免过多线程竞争CPU资源。
- 最大线程数(maximumPoolSize):考虑系统资源限制,包括CPU、内存等。例如,在一个内存有限的系统中,不能设置过大的最大线程数,防止内存溢出。一般可以在核心线程数基础上,根据系统资源情况适当增加一定比例,如增加50%。
- 存活时间(keepAliveTime)和时间单位(unit):对于使用CachedThreadPool场景,适当设置存活时间可以避免线程无限制增长。例如,设置存活时间为5秒,当线程空闲时间超过5秒,就会被回收,这样既可以在任务突发时快速增加线程,又能在任务结束后及时释放资源。
- 工作队列(workQueue):根据任务特性选择合适的队列。对于任务数量相对稳定,对执行顺序有要求的场景,可以选择
ArrayBlockingQueue
,并设置合理的容量,防止任务堆积过多;对于任务量波动较大,且允许一定程度延迟执行的场景,可以选择LinkedBlockingQueue
,但要设置合理的容量,避免内存耗尽。
三、改进方案在不同应用场景下的优势和局限性
- I/O密集型应用场景
- 优势:合理配置核心线程数和最大线程数可以充分利用CPU资源,在I/O等待时可以有更多线程去执行其他任务,提高系统整体吞吐量。例如,在网络爬虫应用中,由于大量时间花费在网络I/O上,增加核心线程数可以同时发起更多请求,加快数据获取速度。
- 局限性:如果线程数设置过多,可能会导致线程上下文切换开销增大,反而降低性能。并且如果工作队列设置不合理,也可能导致任务堆积或资源浪费。
- CPU密集型应用场景
- 优势:将核心线程数设置为CPU核心数,可以避免过多线程竞争CPU资源,提高单个任务的执行效率。例如,在图像渲染应用中,每个任务都需要大量CPU计算,设置合适的线程数可以保证任务高效执行。
- 局限性:由于CPU资源有限,不能通过增加线程数来提高吞吐量。如果任务执行时间较长,且工作队列设置不合理,可能导致新任务长时间等待。
- 任务量波动较大的应用场景
- 优势:通过合理设置存活时间和工作队列,可以在任务高峰时快速增加线程处理任务,在任务低谷时及时回收线程,节省资源。例如,电商系统在促销活动期间任务量剧增,活动结束后任务量减少,这种情况下可以有效利用资源。
- 局限性:如果存活时间设置不合理,可能导致线程频繁创建和销毁,增加系统开销。同时,如果工作队列容量设置不当,在任务高峰时可能会出现任务丢失的情况。