####什么是锁?
锁是一个多方可以共同访问的元素,各个访问者通过对该元素的信息的判断,按照一定事先约定的行为进行协调的功能。这个元素可以是任何的东西,根据事先约定的行为的不同也会随之变化。
例如,一个队列,如果来访者发现自己的id在该队列的头部,那么就认为自己拥有了锁,可以执行某些逻辑,这时候这个队列就相当于一个锁。锁也可以是一个boolean类型的对象,当他为true或者false时,其他线程可以来竞争使得boolean状态改变,从而认为自己获取了锁(当然需要考虑使用场景)。锁也可以是一个信号量,也可以是一个节点,例如zookeeper中,一个节点存在与否就意味着是否可以竞争锁(当然行为是我们自己来定的,zookeeper和redis仅仅是提供了一个放置锁的地方)。当然,最重要的一点就是原子性,我们在加锁和解锁的时候,要充分考虑使用场景来决定对锁的判断策略
####redis锁的实现
对于redis分布式锁来说,常用的莫过于SETNX,SET,DEL这几个函数了
tip:现在SET函数可以传递参数,例如过期时间,在已存在值时的反应,对于添加成功或者失败的返回值这几个元素,所以SET已经完全可以取代SETNX,甚至说比SETNX表现更好。因为SETNX在加锁时还要设置过期时间字段,需要由客户端根据这个字段来判断锁是否过期,这样一牵扯到非原子性的问题,就会十分复杂
最简单的加锁解锁代码如下,由于在解锁时,伴随着锁过期的可能,我们需要先判断锁是否是本客户端加的,再去解锁,否则A加锁,A过期,B加锁,A完成任务解锁,就把B加的锁解掉了。解锁操作我们可以想象,他是先查询再操作,不是原子性,所以我们需要封装LUA脚本来使这两条语句具备原子性
具体SET参数意义可以自行搜索
/**
* Non-blocking try to hold a lock
* if true,the work must be finished within millisecond,else the distributed-lock is meaningless
* @param key the key of lock
* @param value a unique String,it will be used When release
* @param expiration it will expirate after now+expiration
* @return true if access,else false
* */
public boolean tryLock(String key,String value,long expiration){
//try to create a record if not exist
Object res = redisTemplate.execute((RedisCallback) redisConnection -> {
JedisCommands connect = (JedisCommands) redisConnection.getNativeConnection();
//SETNX can be replace by SET from Redis 2.6.12 version