前提
单体服务,该机制建立在单体服务的基础上实现的,如果是分布式服务,建议使用分布式锁。
项目场景
项目问题如下:积分兑换奖品,奖品有库存和兑换限制,每日奖品限量,且每家庭每个奖品只能兑换一次。如果在方法使用synchronized关键字,则所有奖品兑换都会变成串行,导致兑换逻辑排队,在并发兑换时严重影响用户体验。
解决思路:使用ReentrantLock可重入锁来更加灵活的控制加锁粒度。
新的问题:如果直接在逻辑中创建一个新的ReentrantLock并且尝试获取锁,无法指定锁对应的奖品类目,进而无法控制库存。
解决思路:未ReentrantLock设计一个LockHolder,通过LockHolder来根据不同的key获取、使相同的key持有相同的ReentrantLock进而按照key的设计来细粒度的控制锁的粒度。
核心代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
// 一、注入spring容器
@Component
public class LockHolder {
private Logger logger= LoggerFactory.getLogger(getClass());
//二、内部私有化可重入读写锁
private ReadWriteLock lock = new ReentrantReadWriteLock();
//三、从上面的可重入读写锁获取写锁
private Lock writeLock = lock.writeLock();
//四、从上面的可重入读写锁获取读锁
private Lock readLock = lock.readLock();
//五、可重入锁的容器
private Map<String, ReentrantLock>lockMap=new ConcurrentHashMap() ;
// 六、根据指定的key获取一把ReentrantLock,通过读写锁控制线程安全
public ReentrantLock getLock(String key) {
readLock.lock();
try {
ReentrantLock bizLock = lockMap.get(key);
if (bizLock == null) {
readLock.unlock();
writeLock.lock();
try {
readLock.lock();
bizLock = new ReentrantLock();
lockMap.put(key, bizLock);
} finally {
writeLock.unlock();
}
}
return bizLock;
} finally {
readLock.unlock();
}
}
//七、定时清理不再使用的锁,释放内存
public LockHolder() {
new Thread(()->{
while (true){
writeLock.lock();
try {
for (Map.Entry<String, ReentrantLock> entry : lockMap.entrySet()) {
String key = entry.getKey();
ReentrantLock lock = entry.getValue();
if(!lock.isHeldByCurrentThread()){
ReentrantLock remove = lockMap.remove(key);
logger.info("remove lock key={},lock={}",key,remove);
}
}
} finally {
writeLock.unlock();
}
try {
// 10 分钟
Thread.sleep(1000L*60*10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
使用示例(伪代码)
public void useLock() {
ReentrantLock lock = lockHolder.getLock("指定key");
try {
lock.lock();
//执行逻辑
} finally {
lock.unlock();
}
}
代码已经放在了gitee,项目地址:lockTool/src/main/java/com/zidong/lockToll/lock/LockHolder.java · DragonJiang/toolSet - Gitee.com
同时该项目包含一些其他实用的工具,欢迎使用。
1380

被折叠的 条评论
为什么被折叠?



