大家好,我是欧阳方超,公众号同名。
1 概述
在上一篇博文中,通过一个示例演示了多线程带来的性能提升,其中里面提到模拟订单验证无需加锁、模拟订单价格计算无需加锁、模拟发送确认邮件无需加锁、扣减库存需要加锁,本篇来解释一下。
2 解释
2.1 为什么扣减库存需要加锁
// 不加锁的错误版本
public boolean deduct(int amount) {
if (stock >= amount) { // 检查库存
// 假设这时线程被中断,其他线程也执行到这里
Thread.sleep(10); // 模拟数据库操作延迟
stock -= amount; // 扣减库存
return true;
}
return false;
}
考虑以下场景:
- 当前库存stock=10
- 订单1要购买7个商品
- 订单2要购买5个商品
线程1执行到if (stock >= amount),发现10 >= 7,准备扣减
同时线程2也执行到if (stock >= amount),发现10 >= 5,也准备扣减
结果:
- 线程1扣减7个,库存变为3
- 线程2扣减5个,库存变为-2
这就出现了超卖问题!
// 正确的加锁版本
public boolean deduct(int amount) {
synchronized (lock) { // 加锁确保原子性
if (stock >= amount) {
stock -= amount;
return true;
}
return false;
}
}
2.2 为什么订单验证无需加锁
private void validateOrder(Order order) {
// 验证订单信息,比如:
boolean isValidAddress = checkAddress(order.getAddress());
boolean isValidPayment = checkPayment(order.getPaymentInfo());
boolean isValidPhone = checkPhone(order.getPhoneNumber());
// 这些操作都只是读取订单自身的信息
// 不会修改任何共享数据
// 每个线程处理的是不同的订单对象
}
原因:
- 只读取当前订单对象的信息,不修改共享数据
- 每个线程处理不同的订单对象,互不影响
- 即使多个线程同时验证不同的订单,也不会产生数据竞争
2.3 为什么价格计算无需加锁
private void calculatePrice(Order order) {
// 假设这是一个复杂的价格计算过程
double basePrice = order.getBasePrice();
double discount = calculateDiscount(order);
double tax = calculateTax(order);
double shippingFee = calculateShipping(order);
double finalPrice = basePrice * (1 - discount) * (1 + tax) + shippingFee;
order.setFinalPrice(finalPrice);
}
原因:
1. 所有计算都基于订单自身的数据
2. 如果需要读取一些公共数据(如折扣规则),通常这些数据是不可变的
3. 计算结果只写入当前订单对象
2.4 为什么发送确认邮件无需加锁
private void sendConfirmationEmail(Order order) {
// 发送邮件的代码
String email = order.getCustomerEmail();
String content = generateEmailContent(order);
emailService.send(email, content);
}
原因:
1. 每个订单发送自己的邮件,互不影响
2. 邮件服务通常是线程安全的
3. 即使多个线程同时发送邮件,也不会产生数据竞争
介绍了,为什么模拟订单验证无需加锁、模拟订单价格计算无需加锁、模拟发送确认邮件无需加锁,而扣减库存需要加锁。
我是欧阳方超,把事情做好了自然就有兴趣了,如果你喜欢我的文章,欢迎点赞、转发、评论加关注。我们下次见。