前言
使用乐观锁能够在不上锁的情况下实现线程安全,常用的实现方式就是使用CAS自旋的形式实现。
通常在分布式系统中有三种实现方式
数据库行级锁
Zookeeper实现分布锁
Redis实现乐观锁
三种方式各有优点,其中Redis和Zookeeper最常用,Redis性能最高,Zookeeper可靠性最高
代码实现
基于RedisTemplate
,封装使用,通过IOC控制的方式实现:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* Redis的分布式锁工具类
* 利用 setnx 实现乐观锁
*
* @author smoyu
* @Date 2021-08-08 21:50:16
*/
@Component
public class RedisTemplateUtil {
// 锁名称 prefix expire 锁对象
public static final String LOCK_PREFIX = "RedisLock";
// 加锁失效时间,毫秒,通常是业务执行时间的3-5倍
public static final int LOCK_EXPIRE = 2000;
// 轮询次数 根据项目而定
public static final int COUNT = 20;
// 轮询间隔时间 一般在50-100ms,根据项目而定
public static final int INTERVAL = 50;
@Autowired
private RedisTemplate redisTemplate;
/**
* 加锁操作
*
* @author smoyu
* @param key 锁的名称
* @return
*/
public Boolean setLock(String key) {
return redisTemplate.opsForValue().setIfAbsent(key, LOCK_PREFIX, LOCK_EXPIRE, TimeUnit.MILLISECONDS);
}
/**
* 释放锁
* @author smoyu
* @return true or false
*/
public Boolean deleteLock(String key) {
return redisTemplate.delete(key);
}
/**
* 采用轮询的方式去加锁,轮询次数和轮询间隔时间写死
* 尝试去获得锁
*
* @param key
* @return
*/
public Boolean tryCasLock(String key) {
int count = COUNT;//获取总轮询次数
while (count > 0) {
count--;
if (setLock(key)) {
return true;
} else {
try {
Thread.sleep(INTERVAL);
} catch (InterruptedException e) {
e.printStackTrace();
throw new RuntimeException(false, StatusCode.ERROR, "服务器繁忙,请稍后再试。");
}
}
}
//如果一直没有取得锁,就返回false
return false;
}
}
使用:调用tryCasLock上锁,deleteLock解锁,解锁操作放在finnally
@Service
public class OrderServiceImpl implements OrderService {
//分布式锁
@Autowired
private RedisTemplateUtil redisLockUtils;
/**
* 抢单
*
* @param order
*/
@Override
public void add() {
//分布式锁,解决超额抢单问题
if (redisLockUtils.tryCasLock(courseId + "")) {
try{
//这里写业务
} finally {
//释放锁
redisLockUtils.deleteLock(courseId + "");
}
}
}
}