Java @PutMapping并发请求上的Spring@Service线程安全问题

Java @PutMapping并发请求上的Spring@Service线程安全问题,java,spring,spring-boot,Java,Spring,Spring Boot,我们有一个如下所示的控制器: @RestController() public class TestController { @Autowired TestService testService; .... // Update request @PutMapping("/update") public ResponseEntity<Sample> updateApi(@PathVariable(value = "id") Long id, @

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

@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);
  }

....
  • 在上面的服务(TestService)中,对jpa存储库的setName和setBody方法调用:这些步骤是否保持状态(即非无状态),并且在处理并发请求时可能导致问题。或者我们需要将此服务的范围(或服务中的updateSample方法)设置为“web感知Spring应用上下文”吗
  • 与第1点类似,这一行:“Sample Sample=apiRepository.findById(sampleId);”,这一行是线程安全的,还是在处理并发请求时会导致问题

  • 那么,让我们深入了解如何使用您的示例:

  • 通常应用服务器
    (tomcat、undertown、jetty等)
    管理一个线程池,每个请求都由一个安全的线程处理
  • 当用户请求进入
    控制器时,将创建一个线程安全,并将其自身保持,直到控制器返回响应以完成请求。因此,请求在
    服务
    中执行一些逻辑,调用
    存储库
    将处于线程安全状态
  • 您可以使用stress
    (Gatling、JMeter或其他)的一些工具用并发请求测试这个简单的代码
    ,您不会有任何问题,因为应用程序服务器创建了一个线程池来管理并发处理的线程安全库,以及它们使用的所有共享资源(服务、存储库等)必须确保螺纹安全
    代码中没有任何线程之间共享的状态。 除非您的存储库不返回一些可以在几个线程之间共享的缓存对象

    如果存储库对每个
    findById
    方法调用执行DB调用,并创建新的
    Sample
    对象,则该对象仅对当前线程可见。


    但您遇到了问题,因为服务的方法
    updateSample
    在事务范围外执行,数据库中的数据可以由另一个线程或进程在
    findById
    save

    之间更改,需要考虑两件事:1)数据库隔离级别,锁定2)
    @Transactional
    -如果您的方法上没有该注释,那么这些调用将在不同的事务中进行。此代码是完全线程安全的。只要不将状态保持在类级别,它就没有问题,不将状态保持在类级别=>这是否意味着在方法中创建的变量是线程安全的?在给定的时间点上,只有一个线程将执行该方法?您的bean是原型还是单线程?我认为这种方法不是“线程安全的”。线程A和线程B修改同一个sampleId对象,数据库中的结果可能不是您想要的结果。这是一个单例,应该是解决“updateSample”中线程问题的最佳方法:我们应该将作用域设置为请求感知还是只同步语句集?@Neo您可以使用
    @Transaction
    注释标记该方法,并使用乐观/悲观锁定来解决并发修改问题
    @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);
      }