Java Spring Boot MVC 控制器是多线程的吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30171027/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 09:16:10  来源:igfitidea点击:

Is Spring Boot MVC controller multithreaded?

javamultithreadingspring-mvcspring-boot

提问by PaintedRed

I'm new to Spring MVC and I didn't find anything about this in doc. Let's say I have a controller /accounts which accepts POST requests to create an account. Two requests comes (almost) in the same time with the same account id. ASFAIK dispatcherServletmanages the requests.

我是 Spring MVC 的新手,我在文档中没有找到任何关于此的信息。假设我有一个控制器 /accounts,它接受 POST 请求来创建一个帐户。两个请求(几乎)同时使用相同的帐户 ID。ASFAIKdispatcherServlet管理请求。

Is the 2nd request put in queue until the first one has been completed? Or there would be two threads working with two requests simultaneously?

第二个请求是否排入队列直到第一个请求完成?或者会有两个线程同时处理两个请求?

UPDATE:Check the official Spring tutorial: Building a REST service. There is controller with method add:

更新:查看官方 Spring 教程:构建 REST 服务。有控制器方法add

@RequestMapping(method = RequestMethod.POST)
ResponseEntity<?> add(@PathVariable String userId, @RequestBody Bookmark input) {
    this.validateUser(userId);
    return this.accountRepository
            .findByUsername(userId)
            .map(account -> {
                Bookmark result = bookmarkRepository.save(new Bookmark(account,
                        input.uri, input.description));

                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.setLocation(ServletUriComponentsBuilder
                        .fromCurrentRequest().path("/{id}")
                        .buildAndExpand(result.getId()).toUri());
                return new ResponseEntity<>(null, httpHeaders, HttpStatus.CREATED);
            }).get();

}

Controllers are just beans, by default they are singleton. When two simultaneous requests are received the same instance of the controller is used by two threads. Let's imagine the 1st thread already saved new bookmark and executing

控制器只是 bean,默认情况下它们是单例的。当接收到两个同时的请求时,两个线程使用相同的控制器实例。让我们想象第一个线程已经保存了新书签并执行

httpHeaders.setLocation(ServletUriComponentsBuilder
                    .fromCurrentRequest().path("/{id}")
                    .buildAndExpand(result.getId()).toUri());

Meanwhile the 2nd thread just executed

同时第二个线程刚刚执行

Bookmark result = bookmarkRepository.save(new Bookmark(account,
                    input.uri, input.description));

In this case the first thread will return result.getId()).toUri()which is created by the 2nd thread.

在这种情况下,第一个线程将返回result.getId()).toUri(),它是由第二个线程创建的。

Is it correct workflow and is this controller thread-safe?

这是正确的工作流程吗?这个控制器线程安全吗?

回答by kaqqao

With all frameworks of that type, it is safe to assume that the controller methods will be processed simultaneously(i.e. in multiple concurrent threads). Furthermore, it is a serious performance hit to do it in any other way (yes, there are frameworks designed very differently from the ground-up, but we're not talking about those now).

对于该类型的所有框架,可以安全地假设控制器方法将同时处理(即在多个并发线程中)。此外,以任何其他方式执行此操作都会严重影响性能(是的,有些框架的设计完全不同,但我们现在不讨论这些)。

Spring itself has no way of knowing that the account id part of the URL is somehow special to synchronize on it. If it would synchronize on URL level, it would bring the performance down to a crawl. You could always implement this logic yourself by, for example, synchronizing on a session-scoped object that this controller method operates on (or make the entire controller session-scoped as Lev's answersuggests). This would then serialize the calls for that user only (that HTTP session). You could have a globally shared lock if you'd want to synchronize between all users, but that's a really bad idea for reasons mentioned above.

Spring 本身无法知道 URL 的帐户 id 部分在某种程度上是特殊的以同步它。如果它会在 URL 级别同步,则会将性能降低到爬行。您始终可以自己实现此逻辑,例如,同步此控制器方法所操作的会话范围对象(或按照Lev 的回答建议将整个控制器设为会话范围)。然后这将仅序列化该用户的调用(该 HTTP 会话)。如果您想在所有用户之间同步,您可以拥有一个全局共享锁,但由于上述原因,这是一个非常糟糕的主意。

Instead, design your application in a way that all controllers are completely stateless and make methods either idempotent if possible or have a correct precondition checking logic. If the method is writing to a DB, you could implement an optimistic-locking strategy. Hibernate already has that feature and some DBs/drivers have it as well, so look into what's applicable to your case.

相反,以所有控制器完全无状态的方式设计您的应用程序,并尽可能使方法具有幂等性或具有正确的前提条件检查逻辑。如果该方法正在写入数据库,则可以实施乐观锁定策略。Hibernate 已经具有该功能,并且某些 DB/驱动程序也具有该功能,因此请查看适用于您的情况的内容。

UPDATE

更新

In the provided example, both threads are saving an instance (I'm assuming save means insert/update as needed) and the returned object is the result of the current save operation, thus no strange inter-thread behavior. This is assuming the save is transactional with a reasonable transaction isolation level (every other assumption would be highly unusual). As it is, the second thread would simply overwrite the values written by the first, but that is probably what you'd expect. That said, you're only reading the id in the following line, and that presumably never changes, so the URL building seems unaffected by concurrency anyway.

在提供的示例中,两个线程都在保存一个实例(我假设 save 意味着根据需要插入/更新)并且返回的对象是当前保存操作的结果,因此没有奇怪的线程间行为。这是假设保存是具有合理事务隔离级别的事务性(所有其他假设都非常不寻常)。实际上,第二个线程会简单地覆盖第一个线程写入的值,但这可能是您所期望的。也就是说,您只是在读取下一行中的 id,并且可能永远不会改变,因此无论如何 URL 构建似乎不受并发性的影响。

回答by breakline

Most servlets start a separate thread for each incoming request and Spring isnt an exception to that. You need to make sure that the shared beans are thread safe. Otherwise Spring takes care of the rest.

大多数 servlet 为每个传入的请求启动一个单独的线程,Spring 也不例外。您需要确保共享 bean 是线程安全的。否则,Spring 会负责其余的工作。

回答by Lev

The solution to the problem may be the following:

该问题的解决方案可能如下:

  1. Annotate Controller with @Scope(“request”) or @Scope(“session”)
  2. Move private variable into one of the methods or save it in session or model
  1. 使用 @Scope(“request”) 或 @Scope(“session”) 注释控制器
  2. 将私有变量移动到其中一种方法中或将其保存在会话或模型中

I got it from there

我从那里得到的