随着业务越来越复杂。应用服务都会朝着分布式、集群方式部署。分布式CAP原则告诉我们,Consistency(一致性) Availability (可用性)、partition tolerance(分区容错性),三者不可得兼。 很多场景中,需要使用分布式事务,分布式锁等技术保证数据最终一致性。有时候,我们需要保证某一个方法同一时刻只能被一个线程执行。 在单机环境中,java提供了很多并发相关API、在多机环境中无能为力了 对于分布式锁,最好能满足以下几点
- 可以保证在分布式部署的应用集群中,同一个方法在同一时间只能被一台机器上一个线程执行
- 这把锁是一个可重入锁(避免死锁)
- 这把锁最好是阻塞锁
- 有高可用获取锁释放锁功能
- 获取锁释放锁性能摇号 针对分布式锁,目前有一下几种方案 www.hollischuang.com/archives/17…
- 基于数据库实现分布式锁
- 基于缓存实现分布式锁
- 基于zookeeper实现分布式锁
分布式同步锁实现 实现思路 锁的实现主要基于redis的 SETNX命令。 我们来看SET NX解释。 SETNX key value 将key的值设为value 当且仅当key不存在 若给定的key存在,则setnx不作任何动作 setnx 是setif not exists 如果不存在 则set简写 返回值 设置成功 返回1 设置失败 返回0
- 使用SETNX 命令获取锁。若返回0 key存在锁存在 则获取失败 繁殖获取成功
- 2 为了防止获取锁程序异常 导致其他线程 进程 调用SETNX命令 总是返回0 进入需要为改key设置一个合理的过期时间
- 释放锁 使用DEL命令将锁删除
实现过程 创建同步锁实现类
/**
* 同步锁
*
* @property key Redis key
* @property stringRedisTemplate RedisTemplate
* @property expire Redis TTL/秒
* @property safetyTime 安全时间/秒
*/
class SyncLock(
private val key: String,
private val stringRedisTemplate: StringRedisTemplate,
private val expire: Long,
private val safetyTime: Long
)
key reids中的key,对应java api synchronized的对象 expire reids中key的过期时间 safetyTime 下文介绍其作用 实现锁的获取功能
private val value: String get() = Thread.currentThread().name
/**
* 尝试获取锁(立即返回)
*
* @return 是否获取成功
*/
fun tryLock(): Boolean {
val locked = stringRedisTemplate.opsForValue().setIfAbsent(key, value) ?: false
if (locked) {
stringRedisTemplate.expire(key, expire, TimeUnit.SECONDS)
}
return locked
}