1 分布式锁具备的条件
(1) 独占性:在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行。
(2) 高可用:不能因某一个节点挂了而出现获取锁和释放锁失败的情况。
(3) 防死锁:要有超时机制或撤销操作兜底释放锁。
(4)不乱抢:自己只能解自己上的锁,不能解其它线程上的锁。
(5) 可重入:同一个节点的同一个线程如果获得锁之后可以再次获取锁。
2 代码实现
@Service
@Slf4j
public class RedisService {
@Autowired
private StringRedisTemplate redisTemplate;
private static final String LOCK = "lock";
private static final Integer LOCK_TIMEOUT = 10;
private static ThreadLocal<String> local = new ThreadLocal<>();
public boolean lock() {
String uuid = getUUID();
local.set(uuid);
Boolean flag = redisTemplate.opsForValue().setIfAbsent(LOCK, uuid, LOCK_TIMEOUT, TimeUnit.SECONDS);
return Boolean.TRUE.equals(flag);
}
public void unlock() {
String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
//脚本返回的是Long
redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList("lock"), local.get());
local.remove();
}
private String getUUID() {
return UUID.randomUUID().toString().replace("-", "");
}
}
3 UT
测试: 10个线程,每个线程给num增加10000,判断最终结果是否为100W。
import com.rosh.RoshSpringApplication;
import com.rosh.service.RedisService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.concurrent.CountDownLatch;
/**
* @Description:
* @Author: rosh
* @Date: 2021/11/17 23:16
*/
@RunWith(SpringRunner.class)
//主application方法
@SpringBootTest(classes = RoshSpringApplication.class)
public class RedisLockTest {
private Integer count = 0;
private static final Integer COUNT_NUM = 10000;
private static final Integer THREAD_COUNTS = 10;
private static final CountDownLatch countDownLatch = new CountDownLatch(THREAD_COUNTS);
@Autowired
private RedisService redisService;
@Test
public void contextLoads() {
for (int i = 1; i <= THREAD_COUNTS; i++) {
new Thread(this::addCount, "thread" + i).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void addCount() {
try {
if (redisService.lock()) {
for (int i = 1; i <= COUNT_NUM; i++) {
count++;
}
countDownLatch.countDown();
System.out.println(Thread.currentThread().getName() + " -> " + count);
} else {
Thread.sleep(500);
addCount();
}
} catch (Exception e) {
throw new RuntimeException("分布式锁抛出异常,异常原因:{}", e.getCause());
} finally {
redisService.unlock();
}
}
}