加群联系作者vx:xiaoda0423
仓库地址:webvueblog.github.io/JavaPlusDoc…
webvueblog.github.io/JavaPlusDoc…
1. 乐观锁(版本号)——库存扣减示例
假设表 product 有 id、stock、version 字段,使用 version 做乐观锁:
@Service
public class InventoryService {
@Autowired
private JdbcTemplate jdbc;
public boolean decreaseStock(Long productId) {
// 1. 读取当前版本号和库存
Map<String, Object> row = jdbc.queryForMap(
"SELECT stock, version FROM product WHERE id = ?", productId);
int stock = (int) row.get("stock");
int version = (int) row.get("version");
if (stock <= 0) {
return false;
}
// 2. 尝试更新:WHERE id=? AND version=?
int updated = jdbc.update(
"UPDATE product SET stock = stock - 1, version = version + 1 " +
"WHERE id = ? AND version = ?", productId, version);
// 3. updated==1 成功,否则认为被并发修改,返回失败
return updated == 1;
}
}
- 阶段:读取→校验(在 SQL 的 WHERE 里)→写入
- 冲突处理:返回
false,可由上层决定重试或提示用户
2. 悲观锁(SELECT FOR UPDATE)——订单处理示例
在一个事务中对库存行加行级锁,避免并发超卖:
@Service
public class OrderService {
@Autowired
private DataSource dataSource;
@Transactional
public void placeOrder(Long productId, int qty) {
// 1. SELECT ... FOR UPDATE 会锁定该行直到事务提交
Integer stock = new JdbcTemplate(dataSource)
.queryForObject(
"SELECT stock FROM product WHERE id = ? FOR UPDATE",
Integer.class, productId);
if (stock == null || stock < qty) {
throw new InsufficientStockException();
}
// 2. 扣减
new JdbcTemplate(dataSource).update(
"UPDATE product SET stock = stock - ? WHERE id = ?",
qty, productId);
// 3. 保存订单等业务逻辑...
}
}
- 锁范围:从
SELECT FOR UPDATE开始直到事务结束 - 注意:长事务会阻塞其他并发请求
3. 分布式锁(Redisson)——分布式库存或任务调度
利用 Redisson 在 Redis 上实现分布式锁:
@Service
public class DistributedInventoryService {
@Autowired
private RedissonClient redisson;
@Autowired
private InventoryService inventoryService;
public boolean safeDecrease(Long productId) {
RLock lock = redisson.getLock("lock:product:" + productId);
// 尝试加锁,等待最多 100ms,锁自动释放时间 5s
try {
if (lock.tryLock(100, 5, TimeUnit.SECONDS)) {
return inventoryService.decreaseStock(productId);
} else {
throw new LockAcquisitionException();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
- 高可用:Redisson 支持可重入、看门狗续期
- 适合:分布式集群 QPS 较高的场景
4. 可重入锁(ReentrantLock)——临界资源保护
保护一段需要互斥访问的业务逻辑:
@Service
public class ReportService {
private final ReentrantLock lock = new ReentrantLock();
public void generateReport() {
lock.lock();
try {
// 临界区:只能一个线程生成报告
doGenerate();
} finally {
lock.unlock();
}
}
private void doGenerate() {
// 具体生成逻辑,可能会递归调用 generateReport()
}
}
- 特点:同一线程可重复获取、支持中断、超时试锁
5. 自旋锁——轻量级互斥示例
简单自旋锁实现,适合持锁时间极短的场景:
public class SimpleSpinLock {
private final AtomicBoolean flag = new AtomicBoolean(false);
public void lock() {
while (!flag.compareAndSet(false, true)) {
// 自旋等待,不让线程进入阻塞
}
}
public void unlock() {
flag.set(false);
}
}
// 使用示例
public class SpinLockService {
private final SimpleSpinLock spinLock = new SimpleSpinLock();
public void doWork() {
spinLock.lock();
try {
// 临界区
} finally {
spinLock.unlock();
}
}
}
6. 读写锁(ReentrantReadWriteLock)——缓存读写场景
@Service
public class CacheService {
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Map<String, String> cache = new HashMap<>();
public String read(String key) {
rwLock.readLock().lock();
try {
return cache.get(key);
} finally {
rwLock.readLock().unlock();
}
}
public void write(String key, String value) {
rwLock.writeLock().lock();
try {
cache.put(key, value);
} finally {
rwLock.writeLock().unlock();
}
}
}
- 读共享:多个线程可同时读
- 写独占:写时阻塞所有读/写
7. 信号量(Semaphore)——限流示例
@Service
public class UploadService {
// 最多允许 3 个并发上传
private final Semaphore sem = new Semaphore(3);
public void upload(Object data) {
try {
sem.acquire();
doUpload(data);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
sem.release();
}
}
}
8. 条件变量(Condition)——生产者-消费者示例
public class ProducerConsumer {
private final ReentrantLock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
private final Queue<String> queue = new LinkedList<>();
public void produce(String item) {
lock.lock();
try {
queue.offer(item);
notEmpty.signal(); // 通知消费者
} finally {
lock.unlock();
}
}
public String consume() throws InterruptedException {
lock.lock();
try {
while (queue.isEmpty()) {
notEmpty.await(); // 释放锁并等待
}
return queue.poll();
} finally {
lock.unlock();
}
}
}
9. 行级锁(数据库内部)——状态流转示例
// 状态只能从 INIT → DOING,重复流转失败
boolean ok = jdbcTemplate.update(
"UPDATE task SET status='DOING' " +
"WHERE id=? AND status='INIT'", taskId) == 1;
if (!ok) {
throw new IllegalStateException("状态流转失败");
}
- 行级锁:由数据库引擎在该记录更新时自动加/释
10. 分段锁(ConcurrentHashMap)——无须额外代码
JDK7/8 的 ConcurrentHashMap 内部通过多段或节点 + CAS 实现高并发访问,无需额外锁管理,直接使用即可:
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("key", 1);
map.computeIfPresent("key", (k,v) -> v+1);