锁机制实现详解

锁的分类体系

分类维度锁类型说明
按操作类型读锁(共享锁)多个事务可同时持有,用于读取
写锁(排他锁)同一时间只能一个事务持有,用于写入
按锁定范围行锁只锁定一行数据
表锁锁定整个表
页锁锁定数据页(介于行锁和表锁之间)
按乐观程度悲观锁假设冲突会发生,先加锁
乐观锁假设冲突少,更新时检查

读写锁(ReadWriteLock)

说明:Java 的 ReadWriteLockJVM 级别的并发控制工具,与数据库完全无关。它属于 java.util.concurrent.locks 包,用于在单进程内实现细粒度的读写分离控制。

ReadWriteLock 接口概述

ReadWriteLock 是 Java 并发包提供的接口,定义了一对锁:

  • 读锁(共享锁):多个线程可同时获取,适合读多写少场景
  • 写锁(排他锁):同一时间只能被一个线程持有

核心特性

  • 读写分离:读操作不阻塞其他读操作,提高并发读性能
  • 写优先/读优先:不同实现有不同的锁获取策略
  • 可重入性ReentrantReadWriteLock 支持锁的重入

Java 读写锁实现

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
 
public class Cache {
    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
    private Map<String, Object> cache = new HashMap<>();
    
    public Object get(String key) {
        rwLock.readLock().lock();
        try {
            return cache.get(key);
        } finally {
            rwLock.readLock().unlock();
        }
    }
    
    public void put(String key, Object value) {
        rwLock.writeLock().lock();
        try {
            cache.put(key, value);
        } finally {
            rwLock.writeLock().unlock();
        }
    }
}

读写锁特性

场景读锁写锁
多个读锁允许阻塞
读锁+写锁阻塞阻塞
多个写锁阻塞阻塞

五、分布式锁实现

5.1 Redis 分布式锁

public class RedisDistributedLock {
    private final StringRedisTemplate redisTemplate;
    private static final String LOCK_PREFIX = "lock:";
    private static final long DEFAULT_EXPIRE = 30000;
    
    public boolean tryLock(String key, long expire) {
        String lockKey = LOCK_PREFIX + key;
        Boolean result = redisTemplate.opsForValue()
            .setIfAbsent(lockKey, UUID.randomUUID().toString(), expire, TimeUnit.MILLISECONDS);
        return Boolean.TRUE.equals(result);
    }
    
    public void unlock(String key) {
        String lockKey = LOCK_PREFIX + key;
        redisTemplate.delete(lockKey);
    }
}

5.2 Zookeeper 分布式锁

// Curator 框架实现
public class ZkDistributedLock {
    private final CuratorFramework client;
    private static final String LOCK_PATH = "/locks/";
    
    public void acquire(String lockName) throws Exception {
        String lockPath = LOCK_PATH + lockName;
        InterProcessMutex mutex = new InterProcessMutex(client, lockPath);
        mutex.acquire();
    }
    
    public void release(String lockName) throws Exception {
        String lockPath = LOCK_PATH + lockName;
        InterProcessMutex mutex = new InterProcessMutex(client, lockPath);
        mutex.release();
    }
}

六、锁机制对比总结

锁类型实现方式并发性能适用场景
悲观锁SELECT FOR UPDATE写多读少、库存扣减
乐观锁版本号/CAS读多写少、普通更新
读写锁ReadWriteLock中高缓存读写分离
分布式锁Redis/ZK跨进程/跨机器

七、实际业务场景示例

场景1:库存扣减(悲观锁)

@Transactional
public boolean purchase(Long productId, int quantity) {
    Stock stock = stockRepository.findByProductIdForUpdate(productId);
    
    if (stock.getCount() < quantity) {
        throw new BusinessException("库存不足");
    }
    
    stock.setCount(stock.getCount() - quantity);
    stockRepository.save(stock);
    
    orderService.createOrder(productId, quantity);
    return true;
}

场景2:用户积分更新(乐观锁)

@Transactional
public boolean updatePoints(Long userId, int points) {
    int retryCount = 3;
    
    while (retryCount > 0) {
        User user = userRepository.findById(userId).orElse(null);
        
        int currentPoints = user.getPoints();
        user.setPoints(currentPoints + points);
        
        try {
            userRepository.save(user);
            return true;
        } catch (OptimisticLockingFailureException e) {
            retryCount--;
            if (retryCount == 0) {
                throw new BusinessException("更新失败,请重试");
            }
        }
    }
    return false;
}

参考链接