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);
}
....
那么,让我们深入了解如何使用您的示例:
(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);
}