自定义分段锁的实现
内容纲要
业务背景
- 在记账软件中,每个用户的统计信息需要存放与redis中,每一次修改或者新增数据后都需要对redis进行更新操作,更新的话需要先获取到redis中的值,然后在对redis进行更新,由于两个操作并不是原子操作,所以需要在java代码中进行锁的操作才能实现
全局锁
- 使用全局锁的话则需要在方法之上添加
synchronized
关键字或者使用Lock
类进行加锁操作 - 使用全局锁之后,那么在jvm中只有一个线程能够对当前方法进行操作,这样的话就影响性能以及吞吐量,如果能够对单独的一个用户进行加锁的话,这样并不会影响到redis中的计算结果,并且可以多个用户进行同时的计算,提高系统的吞吐量
分段锁
- 分段锁的实现则是对每个用户进行单独的加锁操作, 具体实现原理则是使用synchronized关键词可以指定具体的锁对象,synchronized关键词的锁必须为final对象,那么在jvm中String对象则满足对象的条件,只需要将用户id转为String类型,并添加到一个缓存中,每次加锁只需要获取到对应的用户即可,具体代码如下
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author Clay
* @date 2023-09-06
*/
public class SegmentLock {
//做好用户Id Long类型到String类型的缓存,String类型为final类型,所以用户id唯一的情况下锁也是唯一的
private final Map<Long, String> userLock = new ConcurrentHashMap<>();
/**
* 分段锁的具体实现
* @param task 在分段锁中需要执行的方法
*/
public void segmentLock(Task task) {
//从Spring Security中获取到当前用户的id信息
Long userId = SecurityUtils.getUserId();
//使用双重锁检查机制检查用户锁中是否已经存在当前用户的String类型
if (!userLock.containsKey(userId)) {
synchronized (userLock) {
if (!userLock.containsKey(userId)) {
userLock.put(userId, userId.toString());
}
}
}
//使用当前用户的String类型作为锁参数进行加锁操作
synchronized (userLock.get(userId)) {
task.run();
}
}
/**
* 任务执行类
*/
public interface Task {
void run();
}
}
其他扩展
- 在只需要对某一个维度下的数据进行操作的时候,其他维度的数据操作并不会对当前维度的数据造成任何影响时,则可以使用此维度的id作为分段锁的唯一标识,从而实现分段锁
共有 0 条评论