You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot:如何确保Bean创建与销毁在同一线程执行

Solution for Ensuring Bean Initialization and Destruction on the Same Thread

Hey there! I get your frustration with this third-party library's strict thread requirement—nothing worse than unexpected JVM crashes and disk space bloat from dump files. Let's fix this by making sure both your Engine initialization and deinitialization run on the exact same thread.

The Core Issue

By default, Spring runs bean initialization during container startup (usually on the main thread or a container-managed thread) and @PreDestroy methods during shutdown on a different thread. That's why your current setup violates the library's thread consistency rule.

Step-by-Step Fix

We'll use a single-threaded executor to bind both operations to the same thread. Here's how to implement it:

  • Define a singleton single-threaded executor: This ensures all tasks submitted to it run sequentially on the same thread.
  • Run initialization in the executor: Instead of initializing the engine directly in the @Bean method, submit the initialization task to our executor and wait for the result.
  • Run deinitialization in the same executor: Submit the deinitialization task to the same executor during the bean's destruction phase.

Full Code Implementation

import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

@Configuration
public class EngineConfig {

    // Single-threaded executor to enforce thread consistency
    @Bean(destroyMethod = "shutdown")
    public ExecutorService engineThreadExecutor() {
        return Executors.newSingleThreadExecutor();
    }

    @Bean
    public IEngine ocrEngine(ExecutorService engineThreadExecutor) throws Exception {
        // Submit initialization task to our dedicated thread
        Future<IEngine> initFuture = engineThreadExecutor.submit(() ->
                Engine.InitializeEngine(
                        sdkLibraryPath,
                        projectId,
                        sdkLicensePath,
                        sdkLicensePassword,
                        "",
                        "",
                        false
                )
        );

        // Wait for initialization to complete (adjust timeout as needed)
        try {
            return initFuture.get(10, TimeUnit.SECONDS);
        } catch (Exception e) {
            // Clean up executor if initialization fails
            engineThreadExecutor.shutdownNow();
            throw new RuntimeException("Failed to initialize OCR engine", e);
        }
    }

    @Bean
    public EngineCleanup engineCleanup(ExecutorService engineThreadExecutor) {
        return new EngineCleanup(engineThreadExecutor);
    }

    // Custom cleanup bean to handle deinitialization in the same thread
    public static class EngineCleanup implements DisposableBean {
        private final ExecutorService executor;

        public EngineCleanup(ExecutorService executor) {
            this.executor = executor;
        }

        @Override
        public void destroy() throws Exception {
            // Submit deinitialization task to the same thread
            Future<Void> destroyFuture = executor.submit(() -> {
                Engine.DeinitializeEngine();
                return null;
            });

            // Wait for deinitialization to finish before shutting down executor
            try {
                destroyFuture.get(5, TimeUnit.SECONDS);
            } finally {
                executor.shutdown();
                executor.awaitTermination(10, TimeUnit.SECONDS);
            }
        }
    }
}

Key Notes

  • Single-threaded Executor: The newSingleThreadExecutor() guarantees all tasks run on the same thread, so initialization and deinitialization are thread-consistent.
  • Timeout Handling: Added timeouts to get() calls to prevent your application from hanging if the library gets stuck during init/destroy.
  • Cleanup Order: We wait for deinitialization to complete before shutting down the executor, ensuring the library's cleanup logic finishes properly.
  • Error Handling: If initialization fails, we shut down the executor immediately to avoid leaving idle threads.

This setup will ensure your Engine is initialized and deinitialized on the exact same thread, eliminating the JVM crash risk and the resulting dump file bloat.

内容的提问来源于stack exchange,提问作者stirante

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 13:07:34