面试题答案
一键面试Java固定数目线程池对线程生命周期的管理
- 线程创建:
- 当固定数目线程池(如
Executors.newFixedThreadPool(int nThreads)
创建的线程池)初始化时,会立即创建nThreads
个线程并将它们添加到线程池中,这些线程处于就绪状态,等待任务分配。 - 例如:
- 当固定数目线程池(如
ExecutorService executorService = Executors.newFixedThreadPool(5);
- 这里会立即创建5个线程。
- 线程复用:
- 当有任务提交到线程池时,线程池会从空闲线程队列中选择一个线程来执行任务。执行完当前任务后,该线程不会被销毁,而是再次回到空闲线程队列,等待下一个任务。
- 比如有一系列任务
Task1
,Task2
,Task3
提交到线程池,线程Thread1
执行Task1
后,不会被销毁,而是等待执行Task2
或Task3
等后续任务。
- 线程销毁:
- 当调用线程池的
shutdown()
方法时,线程池不再接受新任务,已提交的任务会继续执行。当所有任务执行完毕,线程池中的线程会被销毁。 - 如果调用
shutdownNow()
方法,线程池会尝试停止所有正在执行的任务,停止等待任务的处理,并返回等待执行的任务列表,然后销毁线程。
- 当调用线程池的
对应用程序性能的影响
- 正面影响:
- 提高响应速度:由于线程是复用的,无需每次执行任务都创建新线程,减少了线程创建和销毁的开销,使得任务可以更快地开始执行,提高了应用程序的响应速度。例如在高并发的网络请求处理场景中,能够快速响应请求。
- 资源控制:固定数目线程池可以控制并发执行的线程数量,避免因线程过多导致系统资源耗尽(如内存溢出等问题),从而保证系统的稳定性。比如在数据库连接操作中,限制线程数量可以防止过多的数据库连接导致数据库服务器压力过大。
- 负面影响:
- 线程饥饿:如果任务执行时间较长,而线程数量有限,可能会导致新提交的任务长时间等待,出现线程饥饿现象,影响应用程序的整体性能。例如在一个计算密集型的任务场景中,有限的线程长时间忙于计算,新的任务无法及时得到处理。
- 资源浪费:如果线程数量设置不合理,过少的线程可能无法充分利用系统资源,导致资源闲置;过多的线程又可能会增加线程上下文切换的开销,降低系统性能。
优化方法
- 合理设置线程数量:
- 根据应用程序的类型(CPU密集型或I/O密集型)来设置线程数量。对于CPU密集型任务,线程数量应接近CPU核心数,可使用
Runtime.getRuntime().availableProcessors()
获取CPU核心数,公式为N = CPU核心数
。对于I/O密集型任务,由于I/O操作时线程会处于等待状态,可设置更多的线程,公式为N = CPU核心数 * (1 + 平均I/O等待时间 / 平均CPU计算时间)
。
- 根据应用程序的类型(CPU密集型或I/O密集型)来设置线程数量。对于CPU密集型任务,线程数量应接近CPU核心数,可使用
- 任务优先级处理:
- 可以通过自定义线程池(如
ThreadPoolExecutor
)并结合优先级队列来实现任务优先级。将任务按照优先级排序,高优先级任务优先执行,减少线程饥饿现象。例如在一个消息处理系统中,重要消息的处理任务优先级设置较高,优先得到执行。
- 可以通过自定义线程池(如
- 动态调整线程池:
- 使用可动态调整线程数量的线程池,如
ThreadPoolExecutor
,通过setCorePoolSize()
和setMaximumPoolSize()
方法根据系统负载动态调整线程数量。在系统负载较低时减少线程数量,降低资源消耗;在负载较高时增加线程数量,提高处理能力。
- 使用可动态调整线程数量的线程池,如