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

Prototype Bean间共享Bean是否线程安全?多线程统计方案咨询

Hey there! Let's tackle your two questions about Spring beans and thread safety head-on:

1. Are shared beans among Prototype Beans thread-safe?

Short answer: It depends entirely on the shared bean's design.

Here's the breakdown:

  • Prototype beans are instantiated every time you request them from the Spring context, so each gets its own unique instance.
  • But if your prototype beans inject a singleton bean (Spring's default scope), that singleton is shared across all prototype instances and every thread using them.
    • If the shared singleton is stateless (no mutable instance variables, only stateless methods), it's inherently thread-safe—no shared state means no race conditions to worry about.
    • If the singleton is stateful (has variables that get updated during execution, like your planned counter), it's not thread-safe by default. Multiple threads from your thread pool could modify the same state at the same time, leading to incorrect values (e.g., missed increments or duplicate counts).
2. Using a Singleton Bean injected into Prototype Beans for statistics (with example code)

This approach is actually a solid choice! Singletons are perfect for holding global, shared state like execution stats—since they're a single instance across the application, all prototype tasks can report to the same stats container seamlessly.

Example Implementation

Let's flesh out the code you started:

First, the singleton stats bean (thread-safe using atomic classes to avoid manual locking):

import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.stereotype.Component;

@Component
public class TaskStatsTracker {
    private final AtomicInteger totalRuns = new AtomicInteger(0);
    private final AtomicInteger successfulRuns = new AtomicInteger(0);
    private final AtomicInteger failedRuns = new AtomicInteger(0);

    // Thread-safe increment methods
    public void markRunStarted() {
        totalRuns.incrementAndGet();
    }

    public void markRunSuccess() {
        successfulRuns.incrementAndGet();
    }

    public void markRunFailure() {
        failedRuns.incrementAndGet();
    }

    // Getters to retrieve stats
    public int getTotalRuns() {
        return totalRuns.get();
    }

    public int getSuccessfulRuns() {
        return successfulRuns.get();
    }

    public int getFailedRuns() {
        return failedRuns.get();
    }
}

Next, the prototype task bean (injects the singleton stats tracker via constructor injection—Spring's preferred practice):

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("prototype")
public class PrototypeTask {
    private final TaskStatsTracker statsTracker;

    public PrototypeTask(TaskStatsTracker statsTracker) {
        this.statsTracker = statsTracker;
    }

    public void execute() {
        statsTracker.markRunStarted();
        try {
            // Your actual task logic goes here
            System.out.printf("Running task from prototype instance %d%n", this.hashCode());
            // Simulate work with a short sleep
            Thread.sleep(100);
            statsTracker.markRunSuccess();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            statsTracker.markRunFailure();
        } catch (Exception e) {
            statsTracker.markRunFailure();
            e.printStackTrace();
        }
    }
}

Finally, the thread pool usage to run prototype tasks:

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class TaskExecutorApp {
    public static void main(String[] args) throws InterruptedException {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        ExecutorService threadPool = Executors.newFixedThreadPool(4);

        // Submit 10 unique prototype tasks to the pool
        for (int i = 0; i < 10; i++) {
            PrototypeTask task = context.getBean(PrototypeTask.class);
            threadPool.submit(task::execute);
        }

        // Shutdown pool and wait for all tasks to finish
        threadPool.shutdown();
        threadPool.awaitTermination(2, TimeUnit.MINUTES);

        // Print final execution stats
        TaskStatsTracker stats = context.getBean(TaskStatsTracker.class);
        System.out.println("\n--- Task Execution Stats ---");
        System.out.printf("Total runs: %d%n", stats.getTotalRuns());
        System.out.printf("Successful runs: %d%n", stats.getSuccessfulRuns());
        System.out.printf("Failed runs: %d%n", stats.getFailedRuns());
    }
}

Key Notes for This Approach

  • Thread Safety: Use java.util.concurrent.atomic classes or ConcurrentHashMap (for more complex stats like per-task timing) to avoid race conditions without messy synchronized blocks.
  • Prototype Scope Best Practice: Always fetch the prototype bean from the Spring context for each task—don't reuse the same prototype instance across threads (that would defeat the purpose of prototype scope and introduce thread safety issues in the prototype itself).
  • Avoid Singleton → Prototype Leaks: Never store a reference to a prototype bean inside the singleton stats tracker. That would lock the prototype instance to the singleton, making it effectively a singleton too.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 11:02:10