
001/*- 002 * #%L 003 * HAPI FHIR Storage api 004 * %% 005 * Copyright (C) 2014 - 2025 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.util; 021 022import ca.uhn.fhir.jpa.search.reindex.BlockPolicy; 023import jakarta.annotation.Nonnull; 024import org.apache.commons.lang3.Validate; 025import org.springframework.core.task.TaskDecorator; 026import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 027 028import java.util.concurrent.RejectedExecutionHandler; 029 030public final class ThreadPoolUtil { 031 private ThreadPoolUtil() {} 032 033 /** 034 * Creates a fixed-size thread pool with {@literal thePoolSize} threads 035 * and an unlimited-length work queue. 036 * 037 * @param thePoolSize The number of threads in the pool 038 * @param theThreadNamePrefix Threads in the pool will be named with this prefix followed by a dash and a number 039 * 040 * @since 8.4.0 041 */ 042 @Nonnull 043 public static ThreadPoolTaskExecutor newThreadPool(int thePoolSize, String theThreadNamePrefix) { 044 return newThreadPool(thePoolSize, thePoolSize, theThreadNamePrefix, 0); 045 } 046 047 @Nonnull 048 public static ThreadPoolTaskExecutor newThreadPool( 049 int theCorePoolSize, int theMaxPoolSize, String theThreadNamePrefix) { 050 return newThreadPool(theCorePoolSize, theMaxPoolSize, theThreadNamePrefix, 0); 051 } 052 053 @Nonnull 054 public static ThreadPoolTaskExecutor newThreadPool( 055 int theCorePoolSize, int theMaxPoolSize, String theThreadNamePrefix, int theQueueCapacity) { 056 return newThreadPool( 057 theCorePoolSize, theMaxPoolSize, theThreadNamePrefix, theQueueCapacity, null, new BlockPolicy()); 058 } 059 060 @Nonnull 061 public static ThreadPoolTaskExecutor newThreadPool( 062 int theCorePoolSize, 063 int theMaxPoolSize, 064 String theThreadNamePrefix, 065 int theQueueCapacity, 066 RejectedExecutionHandler theRejectedExecutionHandler) { 067 return newThreadPool( 068 theCorePoolSize, 069 theMaxPoolSize, 070 theThreadNamePrefix, 071 theQueueCapacity, 072 null, 073 theRejectedExecutionHandler); 074 } 075 076 @Nonnull 077 public static ThreadPoolTaskExecutor newThreadPool( 078 int theCorePoolSize, 079 int theMaxPoolSize, 080 String theThreadNamePrefix, 081 int theQueueCapacity, 082 TaskDecorator taskDecorator, 083 RejectedExecutionHandler theRejectedExecutionHandler) { 084 Validate.isTrue( 085 theCorePoolSize == theMaxPoolSize || theQueueCapacity == 0, 086 "If the queue capacity is greater than 0, core pool size needs to match max pool size or the system won't grow the queue"); 087 Validate.notBlank(theThreadNamePrefix, "Thread name prefix must not be blank"); 088 String threadNamePrefix = theThreadNamePrefix; 089 if (!threadNamePrefix.endsWith("-")) { 090 threadNamePrefix = threadNamePrefix + "-"; 091 } 092 ThreadPoolTaskExecutor asyncTaskExecutor = new ThreadPoolTaskExecutor(); 093 asyncTaskExecutor.setCorePoolSize(theCorePoolSize); 094 asyncTaskExecutor.setMaxPoolSize(theMaxPoolSize); 095 asyncTaskExecutor.setQueueCapacity(theQueueCapacity); 096 asyncTaskExecutor.setAllowCoreThreadTimeOut(true); 097 asyncTaskExecutor.setThreadNamePrefix(threadNamePrefix); 098 asyncTaskExecutor.setRejectedExecutionHandler(theRejectedExecutionHandler); 099 asyncTaskExecutor.setTaskDecorator(taskDecorator); 100 asyncTaskExecutor.initialize(); 101 return asyncTaskExecutor; 102 } 103}