1, the principle of
The setnx command: setnx hello redis. Short for SET if Not eXists. Returns 0 on failure if the key eXists and 1 on success if the key does Not exist. Because Redis is single-threaded, if multiple clients execute setnx key value at the same time, only one client can be successfully set according to sexNx characteristics. Senx can be used as an implementation of distributed locking.
2. Pay attention
So what are the problems with setnx?
1. This command must set an expire key seconds. If there is no expire key seconds, the threads competing for the lock will never be executed
Setnx and exprie these are two operations and not an atomic operation. What if the setnx command crashes and the lock has no timeout set?
3. How long is the expiration time properly set?
4. The expiration time is up and the lock is released, but the business code is not finished. What should I do?
5. You might say voluntarily delete the key and release the lock after executing the business code. If the expiration time is up, the lock will be released. At this time, the business code is not finished executing. When the business code is finished executing, the key will be deleted to release the lock. So it’s not so easy to control a lock using setnx, as shown in the code example below
3. Code examples
* @param acquireTimeout Specifies the timeout period for obtaining the lock. * @param lockTimeout Specifies the expiration time of the lock. * @return */ public String acquireLock(String lockName, long acquireTimeout, String identifier = uuid.randomuuid ().toString(); String lockKey = "lock:" + lockName; int lockExpire = (int) (lockTimeout / 1000); Jedis jedis = null; try { jedis = JedisConnectionUtils.getJedis(); long end = System.currentTimeMillis() + acquireTimeout; While (system.currentTimemillis () < end) {if (jedis.setnx(lockKey, Identifier) == 1) {// Set a reasonable expiration time when acquiring a lock, so that the lock can be released correctly even if the server goes down. jedis.expire(lockKey, lockExpire); // Set timeout period return identifier; } // Otherwise, determine the validity of the key. // the TTL command returns the remaining expiration time of the key in seconds. // Return -2 if key does not exist. If the key exists but the remaining lifetime is not set, -1 is returned. Otherwise, the remaining lifetime of the key is returned, in milliseconds. if (jedis.ttl(lockKey) == -1) { jedis.expire(lockKey, lockExpire); Thread.sleep(100); thread.sleep (100); thread.sleep (100); } catch (InterruptedException e) { e.printStackTrace(); } } } finally { jedis.close(); } return null; }} public Boolean releaseLock(String lockName, String lockName, String lockName, String lockName) String identifier) {system.out.println (lockName + "start release lock:" + identifier); String lockKey = "lock:" + lockName; Jedis jedis = null; boolean isRelease = false; try { jedis = JedisConnectionUtils.getJedis(); While (true) {// In Redis, use the watch command to implement an optimistic lock (watch key): Jedis. Watch (lockKey); jedis. Watch (lockKey); If (identifier. Equals (jedis.get(lockKey))) {Transaction = jedis.multi(); transaction.del(lockKey); // Commit the transaction and return null if (transaction.exec().isempty ()) {continue; } isRelease = true; } jedis.unwatch(); break; } } finally { releaseLock(lockKey,identifier); jedis.close(); } return isRelease; }Copy the code
4, the subsequent
This amount of code is actually quite a lot, there are better ways to implement distributed lock, Redisson to implement distributed lock, so these details do not need to be considered, the code is more concise and clean, later we will discuss, Redisson to implement distributed lock