假设我有两个API:
获取客户姓名 另一个用于更新客户名称
我同时有两个更新客户名称的请求, 如何自动处理这种并发并且不发生竞争条件?
因为我没有看到任何现实世界的例子在这种情况下使用同步。
你能帮我解释一下吗?
@RestController
@RequestMapping("/api/customers")
public class CustomerController {
@Autowired
private CustomerService customerService;
@GetMapping("/{id}")
public String getCustomerName(@PathVariable Long id) {
return customerService.getCustomerName(id);
}
@PutMapping("/{id}")
public String updateCustomerName(
@PathVariable Long id,
@RequestParam String name,
@RequestParam int version) {
return customerService.updateCustomerName(id, name, version);
}
}
这里没有竞争条件。 update 会做一些事情,get 会看到或看不到它。因为这两个调用之间没有关系,所以不需要遵守某些顺序。
customerService
类可能无法处理使用与另一个线程中当前并发运行的getCustomerName(id)
调用相同的id调用其updateCustomerName(thatId)
方法,在这种情况下,您需要提出一个不同的问题,其中包括代码。
出现多核问题的地方例如是这样的问题:
PUT /1234
,将客户名称更新为 newName
。该客户等待该调用完成,然后再次连接到我的 API,这次发送 HTTP 请求GET /1234
。我想告诉该用户,我们保证他们观察到的状态至少与他们的更新一样新,即如果名称之前是 oldName
,我们绝对不会将其发回。仅基于您粘贴的代码,您可以不提供该保证。提供这些保证的方法几乎总是归结为数据库:它根本与 java 代码无关:您不需要
synchronized
或任何其他并发原语。相反,正确设置数据库并正确使用事务。
如果你想仅用这个java代码提供保证,你必须确保
getCustomerName
行和updateCustomerName
行都位于synchronized(x)
块内,其中x
指向同一个对象。您可以为此使用 this
(即将两种方法标记为 synchronized
)。但这通常不是您做这样的事情的方式,并且通常数据库对锁定提供更细粒度的控制;例如,DB可以进行乐观锁定; java的同步原语不允许这样做;你必须使用java的CAS(比较和设置,用于乐观锁定的通用机制)系统,对于你在这里做的事情来说,它比数据库事务更难使用。