如何并行初始化150个EntityManagerFactory?
并行初始化多个EntityManagerFactory的解决方案
问题根源
你当前的代码看似用了线程池,但实际无法实现并行,核心问题有两个:
- 共享配置对象引发线程安全问题:直接修改
persistenceUnitInfo.getProperties()返回的Properties实例,该实例可能被多个线程共享,导致配置修改时的竞争,最终阻塞初始化流程。 - 未等待任务完成就关闭线程池:
try-with-resources会在代码块结束时立即关闭线程池,此时提交的初始化任务大概率还未执行完成,要么被中断,要么无法正常完成初始化。
修复后的并行初始化代码
import org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl; import org.hibernate.jpa.boot.internal.PersistenceUnitInfoDescriptor; import javax.persistence.spi.PersistenceUnitInfo; import java.util.HashMap; import java.util.List; import java.util.Properties; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public abstract class Test { private void initManagerFactories() { List<PersistenceUnitInfo> persistenceUnitInfos = getPersistenceUnitInfos(); // 线程池大小不宜设置为150,避免耗尽系统资源,建议用CPU核心数的2倍作为上限 int poolSize = Math.min(Runtime.getRuntime().availableProcessors() * 2, persistenceUnitInfos.size()); try (ExecutorService executorService = Executors.newFixedThreadPool(poolSize)) { for(PersistenceUnitInfo persistenceUnitInfo : persistenceUnitInfos) { // 每个任务使用独立的Properties对象,避免多线程共享修改 Properties properties = new Properties(); properties.putAll(persistenceUnitInfo.getProperties()); properties.setProperty("hibernate.current_session_context_class", "thread"); properties.setProperty("hibernate.hql.bulk_id_strategy", "org.hibernate.hql.spi.id.inline.InlineIdsOrClauseBulkIdStrategy"); properties.setProperty("hibernate.hbm2ddl.auto", "update"); properties.setProperty("hibernate.hbm2ddl.jdbc_metadata_extraction_strategy", "individually"); properties.setProperty("hibernate.order_inserts", "true"); properties.setProperty("org.hibernate.envers.store_data_at_delete", "true"); properties.setProperty("hibernate.search.backend.hosts", "localhost:9200"); properties.setProperty("hibernate.search.backend.protocol", "http"); properties.setProperty("hibernate.search.backend.schema_management.settings_file", "custom/index-settings.json"); executorService.submit(() -> { try { // 执行EntityManagerFactory初始化 new EntityManagerFactoryBuilderImpl( new PersistenceUnitInfoDescriptor(persistenceUnitInfo), new HashMap<>() ).build(); } catch (Exception e) { // 捕获并处理初始化异常,避免静默失败 e.printStackTrace(); } }); } // 先关闭线程池接收新任务,再等待所有已提交任务完成 executorService.shutdown(); try { // 设置超时时间,根据实际情况调整(比如150个实例,单个10秒,设置30分钟足够) if (!executorService.awaitTermination(30, TimeUnit.MINUTES)) { // 超时后强制终止未完成的任务 executorService.shutdownNow(); } } catch (InterruptedException e) { executorService.shutdownNow(); Thread.currentThread().interrupt(); } } } public abstract List<PersistenceUnitInfo> getPersistenceUnitInfos(); }
核心优化说明
- 独立配置实例:每个初始化任务复制一份Properties配置,避免多线程共享修改引发的竞争问题,确保并行初始化的线程安全。
- 合理线程池大小:直接创建150个线程会耗尽内存、数据库连接等系统资源,用
CPU核心数*2作为线程池上限,平衡并行效率和资源消耗。 - 等待任务完成:调用
shutdown()和awaitTermination(),确保所有EntityManagerFactory初始化完成后再关闭线程池,避免任务被中断。 - 异常捕获:在任务内部捕获异常,避免单个实例初始化失败导致整个流程静默出错,方便排查问题。
额外注意事项
- 数据库连接池配置:150个EntityManagerFactory会占用大量数据库连接,需确保数据库最大连接数足够,同时每个实例的连接池参数(如
hibernate.c3p0.max_size)要合理配置,避免连接耗尽。 - Hibernate Search资源控制:每个实例会初始化Elasticsearch客户端,并行创建可能给ES带来压力,可调整
hibernate.search.backend.connections.max参数控制ES连接数。
内容的提问来源于stack exchange,提问作者user32575965
相关产品推荐
相关产品推荐

