java Sping Boot MVC控制器是多线程的吗?

ux6nzvsh  于 10个月前  发布在  Java
关注(0)|答案(3)|浏览(76)

我是Spring MVC的新手,我在doc中没有找到任何关于这方面的信息。假设我有一个控制器/帐户,它接受POST请求来创建帐户。两个请求(几乎)同时出现,具有相同的帐户ID。ASFAIK dispatcherServlet管理请求。
是否将第二个请求放入队列中,直到第一个请求完成?或者会有两个线程同时处理两个请求?

**更新:**查看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();

}

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

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


与此同时,第二个线程刚刚执行

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


在这种情况下,第一个线程将返回由第二个线程创建的result.getId()).toUri()
工作流是否正确,此控制器是否线程安全?

i2loujxw

i2loujxw1#

大多数servlet为每个传入的请求启动一个单独的线程,Spring也不例外。您需要确保共享bean是线程安全的。Spring会处理剩下的事情。

q8l4jmvw

q8l4jmvw2#

对于所有该类型的框架,可以安全地假设控制器方法将同时处理(即在多个并发线程中)。此外,以任何其他方式来做这件事都是一个严重的性能打击(是的,有一些框架的设计与从头开始非常不同,但我们现在不谈论这些)。
Spring本身无法知道URL的account id部分在某种程度上是特殊的,因此无法在其上同步。如果它在URL级别上同步,它将使性能下降到爬行。您可以自己实现这个逻辑,例如,在这个控制器方法操作的会话范围的对象上进行同步(或者像Leo's answer建议的那样使整个控制器会话范围)。然后,这将序列化仅针对该用户(该HTTP会话)的调用。如果希望在所有用户之间进行同步,可以使用全局共享锁,但由于上述原因,这是一个非常糟糕的想法。
相反,设计你的应用程序的方式,所有的控制器是完全无状态的,并使方法或者是幂等的,如果可能的话,或者有一个正确的前提检查逻辑。如果方法正在写入数据库,则可以实现乐观锁定策略。Hibernate已经有了这个特性,一些DB/驱动程序也有,所以看看什么适合你的情况。

更新

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

ndh0cuux

ndh0cuux3#

问题的解决方案可能如下:
1.使用@Scope(“request”)或@Scope(“session”)注解Controller
1.将私有变量移动到其中一个方法中或将其保存在会话或模型中
我从there得到的

相关问题