线程池是一种用于管理线程的技术,它可以有效地重用线程、控制线程的数量,从而提高程序的性能和效率。
线程池的运行原理如下:
-
线程池初始化:在程序启动时,线程池会创建一定数量的核心线程,这些线程会一直存活在线程池中,即使它们处于空闲状态也不会被销毁。
-
任务提交:当有任务需要执行时,可以将任务提交给线程池。线程池会根据当前的线程数量和设置的参数来决定如何处理这个任务。
-
任务执行:线程池会优先使用核心线程来执行任务。如果核心线程数已经达到上限,线程池会将任务放入工作队列中等待执行。
-
动态调整线程数量:如果工作队列中的任务过多,线程池会根据设置的最大线程数动态创建新的线程来处理任务。当任务执行完毕后,空闲时间超过一定阈值,多余的线程会被回收销毁,以节省系统资源。
-
拒绝策略:如果线程池无法接受新的任务(例如线程数量已经达到最大值且工作队列已满),则根据设置的拒绝策略来处理这些任务,比如抛出异常、丢弃任务等。
线程池的七个核心参数是:
-
核心线程数(Core Pool Size):线程池中保持活动状态的线程数量,即最小的可用线程数。当有新任务提交时,线程池会创建这些线程,即使当前没有任务需要执行。
-
最大线程数(Maximum Pool Size):线程池中允许存在的最大线程数。当任务数量超过核心线程数并且任务队列已满时,线程池会创建新的线程,直到达到最大线程数。
-
任务队列(Task Queue):用于存放待执行的任务的队列。当任务数量超过核心线程数时,新的任务将被放入任务队列中等待执行。
-
空闲线程存活时间(Keep Alive Time):当线程池中的线程数量超过核心线程数,并且空闲时间达到设置的时间时,多余的线程将被终止并移出线程池,以节省资源。
-
时间单位(Time Unit):用于指定空闲线程存活时间的单位,例如毫秒、秒、分钟等。
-
拒绝策略(Rejected Execution Handler):当任务无法被线程池执行时的处理策略。例如,任务队列已满并且线程池中的线程数已达到最大线程数时,新提交的任务可能会被拒绝执行。常见的拒绝策略有丢弃任务、抛出异常、阻塞等待和调用者运行等。
-
线程工厂(Thread Factory):用于创建新线程的工厂类,可以自定义创建线程的方式。默认情况下,线程池会使用默认的线程工厂来创建线程。
这些参数共同决定了线程池的行为和性能。通过调整这些参数,可以根据具体的需求来优化线程池的效率和资源利用情况。
线程池的饱和策略
线程池的饱和策略是指当线程池中的工作队列已满并且线程池中的线程数达到最大线程数时,决定如何处理新提交的任务的策略。
Java 中的 ThreadPoolExecutor 类提供了四种预定义的饱和策略:
-
AbortPolicy(默认策略):当工作队列已满且线程池中的线程数达到最大线程数时,新提交的任务将会被拒绝,并且抛出 RejectedExecutionException 异常。
-
CallerRunsPolicy:新提交的任务会被当前线程(提交任务的线程)执行,而不会放入工作队列中。这样可以降低新任务的提交速度,但是可能会影响性能。
-
DiscardPolicy:当工作队列已满且线程池中的线程数达到最大线程数时,新提交的任务将会被直接丢弃,不会抛出异常也不会执行。
-
DiscardOldestPolicy:如果工作队列已满,会丢弃队列中等待最久的任务,然后尝试再次提交当前任务。
除了以上四种预定义的饱和策略外,开发者也可以通过实现 RejectedExecutionHandler 接口来定义自定义的饱和策略。通过实现自定义的饱和策略,可以根据具体业务需求来处理无法执行的任务,例如记录日志、重新提交任务等。