【问题标题】:Singleton and Multithread in SpringBoot. Is it really multi thread?Spring Boot 中的单例和多线程。真的是多线程吗?
【发布时间】:2020-08-20 09:12:29
【问题描述】:

由于我不是专门研究多线程的,所以问题可能是低级甚至愚蠢的,请原谅=)

这是我的代码调用流程;

MessageNotificationJobExecutionConfig -> AsyncMessageNotificationJobExecutor -> NotificationJobExecutor.execute()

MessageNotificationJobExecutionConfig(查找要处理的对象)并在循环内调用 AsyncMessageNotificationJobExecutor

AsyncMessageNotificationJobExecutorexecute() 方法上有 @Async("messageNotificationTaskExecutor") 注释。

AsyncMessageNotificationJobExecutor.execute() 方法调用 NotificationJobExecutor.execute()

messageNotificationTaskExecutorThreadPoolTask​​Executor

的一个实例

这是我的问题;

如果没有错,默认 NotificationJobExecutor 有一个 singletone 实例。

即使 AsyncMessageNotificationJobExecutor 异步工作并使用线程池任务执行器,所有线程也只调用 NotificationJobExecutor 实例(单调)。

我不确定,我可能误解了 Thread_1 调用 NotificationJobExecutor.execute() 并且在该线程完成其工作之前,其他线程等待 Thread_1。我的推断正确吗?

我认为即使它看起来是多线程,实际上它也可以单调运行

@Component("messageNotificationTaskExecutor")
public class MessageNotificationThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {

    @Value("${message.notification.task.executor.corePoolSize}")
    Integer corePoolSize;

    @Value("${message.notification.task.executor.maxPoolSize}")
    Integer maxPoolSize;

    @Value("${message.notification.task.executor.queueCapacity}")
    Integer queueCapacity;

    public MessageNotificationThreadPoolTaskExecutor() {
        super();
    }

    @PostConstruct
    public void init() {
        super.setCorePoolSize(corePoolSize);
        super.setMaxPoolSize(maxPoolSize);
        super.setQueueCapacity(queueCapacity);
    }

}
@Configuration
public class MessageNotificationJobExecutionConfig {

    protected Logger log = LoggerFactory.getLogger(getClass());

    @Autowired
    AsyncMessageNotificationJobExecutor asyncMessageNotificationJobExecutor;

    @Autowired
    MessageNotificationThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Autowired
    JobExecutionRouter jobExecutionRouter;

    @Autowired
    NotificationJobService notificationJobService;

    private Integer operationType = OperationType.ACCOUNT_NOTIFICATION.getValue();

    @Scheduled(cron = "${message.notification.scheduler.cronexpression}")
    public void executePendingJobs() {
        
        List<NotificationJob> nextNotificationJobList = notificationJobService.findNextJobForExecution(operationType, 10);

        for (NotificationJob nextNotificationJob : nextNotificationJobList) {
            if (threadPoolTaskExecutor.getActiveCount() < threadPoolTaskExecutor.getMaxPoolSize()) {
                asyncMessageNotificationJobExecutor.execute(nextNotificationJob);
            }
        }
    }
}
@Service
public class AsyncMessageNotificationJobExecutor {
    
    @Autowired
    NotificationJobExecutor notificationJobExecutor;

    @Autowired
    NotificationJobService notificationJobService;
    
    @Async("messageNotificationTaskExecutor")
    public void execute(NotificationJob notificationJob) {
            notificationJobExecutor.execute(notificationJob);
    }
}

@Component
public class NotificationJobExecutor implements JobExecutor {
    
    @Override
    public Integer getOperationType() {
        return OperationType.ACCOUNT_NOTIFICATION.getValue();
    }
    
    @Override
    public String getOperationTypeAsString() {
        return OperationType.ACCOUNT_NOTIFICATION.name();
    }
    
    @Override
    public void execute(NotificationJob notificationJob) {
        // TODO: 20.08.2020 will be execute 
    }
    
}

【问题讨论】:

  • 他们不会等待,你的课堂上没有任何同步。所以允许多个线程调用同一个实例。某物是单例的事实并不意味着一次有 1 个线程可以访问该对象。您的示例过于复杂且令人费解,它已经安排了调用异步方法的内容。所以它是异步的两次。你也不需要子类。

标签: java multithreading spring-boot


【解决方案1】:

在您创建的场景中,您拥有所有单例实例。但流程看起来像这样:

  1. MessageNotificationJobExecutionConfig 中致电executePendingJobs
  2. 依次迭代每个NotificationJob(所以这是等待)
  3. AsyncMessageNotificationJobExecutor 中调用execute,这将向线程池中的messageNotificationTaskExecutor 顺序(因此阻塞)添加一个执行
  4. 在单独的线程中执行步骤 3 中创建的作业(因此这实际上会执行您在 AsyncMessageNotificationJobExecutor 中的方法
  5. NotificationJobExecutor 中对execute 方法的阻塞调用

“魔法”发生在第 3 步,在该方法中,Spring 将向 messageNotificationTaskExecutor 添加一个作业,该作业包含对第 4 步的调用。这会导致对第 4 步的调用异步发生,因此会发生多次调用同一个实例可以同时发生。所以要确保这个对象是无状态的。

【讨论】:

    最近更新 更多