关于@PutMapping并发请求的Spring @Service线程安全问题

时间:2019-01-25 11:05:12

标签: java spring spring-boot

我们有一个如下所示的控制器:

@RestController()
public class TestController {

  @Autowired
  TestService testService;

  ....

  // Update request
  @PutMapping("/update")
  public ResponseEntity<Sample> updateApi(@PathVariable(value = "id") Long id,
      @Valid @RequestBody Sample sampleDetails) {
    return new ResponseEntity<Sample>(testService.updateSample(id, sampleDetails), HttpStatus.OK);
  }

....

服务类(依赖于JPA存储库)如下:

@Service
public class TestService {

  @Autowired
  TestRepository testRepository;

  // update sample
  public Api updateSample(long sampleId, Sample details) {

    // Get object to be updated
    Sample sample = apiRepository.findById(sampleId);

    // Update required fields
    sample.setName(details.getName());
    sample.setBody(details.getBody());
    return apiRepository.save(api);
  }
  1. 在上述service(TestService)中,对jpa存储库的setName和setBody方法调用:执行以下步骤是否保持状态(即非无状态),并且在处理并发请求时可能导致问题。还是我们需要将此服务(或服务中的updateSample方法)的范围设置为“可感知网络的Spring ApplicationContext”
  2. 类似于第1点,此行:“ Sample sample = apiRepository.findById(sampleId);”,此行是线程安全的还是在处理并发请求时会引起问题。

2 个答案:

答案 0 :(得分:1)

您的代码中没有线程之间共享的任何状态。 除非您的存储库不返回某些可以在少数线程之间共享的缓存对象。

如果存储库在每个findById方法调用中都执行DB调用并创建了新的Sample对象,则该对象仅对当前线程可见。 Why are local variables thread safe in Java

但是您有Transaction Isolation问题,因为服务的方法updateSample在事务范围之外执行,并且DB中的数据可以由findByIdsave之间的另一个线程或进程更改。

答案 1 :(得分:0)

好吧,让我们深入探讨如何处理您的示例:

  1. 通常,应用服务器(tomcat, undertown, jetty, others)管理一个线程池,每个请求都由一个安全的线程处理。
  2. 当用户请求输入Controller时,将创建一个线程安全的线程,并一直保留到控制器返回响应以完成请求为止。因此,请求在Service中执行了一些逻辑,并且调用Repository将在线程安全中。
  3. 您可以使用压力测试工具(Gatling, JMeter, or other)对并发请求测试此简单代码,并且不会出现任何问题,因为应用程序服务器创建了一个线程池来管理并发处理的线程安全,并且因此,他们使用的所有共享资源(服务,存储库等)必须确保线程安全。