单例模式和多线程在SpringBoot中。它真的是多线程的吗?

huangapple go评论79阅读模式
英文:

Singleton and Multithread in SpringBoot. Is it really multi thread?

问题

因为我没有明确地处理多线程,所以问题可能比较低级或者有些愚蠢,请原谅我 =)

这是我的代码调用流程:

MessageNotificationJobExecutionConfig -> AsyncMessageNotificationJobExecutor -> NotificationJobExecutor.execute()

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

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

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

messageNotificationTaskExecutorThreadPoolTaskExecutor 的一个实例。

这是我的问题:

如果我没错的话,默认情况下 NotificationJobExecutor 有一个 单例 实例。

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

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

我认为,即使它看起来是多线程,实际上它是单例的。

@Component("messageNotificationTaskExecutor")
public class MessageNotificationThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
    // ... 省略其他代码 ...
}
@Configuration
public class MessageNotificationJobExecutionConfig {
    // ... 省略其他代码 ...
}
@Service
public class AsyncMessageNotificationJobExecutor {
    // ... 省略其他代码 ...
}
@Component
public class NotificationJobExecutor implements JobExecutor {
    // ... 省略其他代码 ...
}
英文:

Since I am not working specifically on multi threads, the questions can be low level or even silly, please excuse me =)

Here is my code call flow like;

MessageNotificationJobExecutionConfig -> AsyncMessageNotificationJobExecutor -> NotificationJobExecutor.execute()

MessageNotificationJobExecutionConfig (finds the objects to process) and calls AsyncMessageNotificationJobExecutor inside the loop

AsyncMessageNotificationJobExecutor has @Async("messageNotificationTaskExecutor") annotation over the execute() method.

AsyncMessageNotificationJobExecutor.execute() method calls NotificationJobExecutor.execute()

messageNotificationTaskExecutor is an instance of ThreadPoolTaskExecutor

Here is my question;

If am not wrong as default NotificationJobExecutor has a singletone instance.

Even if AsyncMessageNotificationJobExecutor work async and use thread pool task executor, all thread call only NotificationJobExecutor instance (singletone).

I am not sure, I may misunderstand that Thread_1 calls NotificationJobExecutor.execute() and until this thread finish its job other thread wait for Thread_1. Is my inference correct ?

I think even if it looks multi thread actually it works singletone

@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

得分: 1

在您创建的场景中,您拥有所有的单例实例。但流程大致如下:

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

"魔法" 发生在第 3 步,此时 Spring 不会执行方法,而是会将一个作业添加到 messageNotificationTaskExecutor,该作业会包装对第 4 步的调用。这将导致对第 4 步的调用异步进行,因此可以同时发生对同一实例的多次调用。因此,请确保此对象是无状态的。

英文:

In the scenario you created you have all singleton instances. But the flow looks something like this:

  1. call to executePendingJobs in MessageNotificationJobExecutionConfig
  2. iterate over each NotificationJob sequentially (so this is waiting)
  3. call to execute in AsyncMessageNotificationJobExecutor which will add a execution to the messageNotificationTaskExecutor sequential (thus blocking) to the thread pool
  4. execute the job created in step 3 in a separate thread (so this actually executes your method in AsyncMessageNotificationJobExecutor
  5. a blocking call to the execute method in NotificationJobExecutor

The 'magic' happens in step 3, where rather then executing the method Spring will add a job to the messageNotificationTaskExecutor which wraps the call to step 4. This causes the call for step 4 to happen asynchronous and thus multiple calls to the same instance can occur at the same time. So make sure this object is stateless.

huangapple
  • 本文由 发表于 2020年8月20日 17:12:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/63501910.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定