Interpretation of redis command
Say Redis Distributed locks are implemented by combining setNx commands with getset. Before we talk about them, let's first understand the meaning of setNx and getset. redis The official website explains this.
Note: redis commands are atomic operations
SETNX key value
Set the value of the key to value if and only if the key does not exist.
If the given key already exists, SETNX does nothing.
SETNX is short for "SET if Not eXists" (SET if it does not exist).
Available Version:
1.0.0+
Time complexity:
O(1)
Return value:
Set up successfully, return 1.
Setup failed, return 0.
redis> EXISTS job # job does not exist (integer) 0 redis> SETNX job "programmer" # job Setup Successful (integer) 1 redis> SETNX job "code-farmer" # Attempt to overwrite job, fail (integer) 0 redis> GET job # Not covered "programmer"
GETSET key value
Set the value of the given key to value and return the old value of the key.
When the key exists but is not a string type, an error is returned.
Available Version:
1.0.0+
Time complexity:
O(1)
Return value:
Returns the old value of the given key.
When the key has no old value, that is, when the key does not exist, it returns nil.
redis> GETSET db mongodb # No old values, return nil (nil) redis> GET db "mongodb" redis> GETSET db redis # Return the old value mongodb "mongodb" redis> GET db "redis"
Code examples
Note: For distributed locks algorithm More stable, before unlocking, the client who holds the lock should check once again whether its lock has expired, and then do DEL operation, because the client may hang up because of a time-consuming operation. When the operation is finished, the lock has been acquired by others, so it is not necessary to unlock.
Let's see that the code involves the following classes. Here, the business logic is related only to defining methods without specific implementation. The key is learning ideas.
OrderBiz.Java
/** * Use redis lock to control concurrent order grabbing * @author fuyuwei */ public class OrderBiz { public int createOrder(){ // Validation of parameters and validity before placing an order is not demonstrated here. OrderLock<Boolean> orderLock = new RedisOrderLock<Boolean>("pro-12345678901"); boolean isSyn = orderLock.isSyn(new OrderLockBiz<Boolean>(){ @Override public Boolean createOrder() { // Neglect creating order logic return null; } }); if(!isSyn){ BizLogger.info("Failure to create an order"); } return 0; } }
OrderLock.java
public interface OrderLock<T> { public boolean isSyn(OrderLockBiz<T> orderBiz); }
OrderLockBiz.java
public interface OrderLockBiz<T> { public T createOrder(); }
RedisOrderLock.java
public class RedisOrderLock<T> implements OrderLock<T> { // Lock waiting timeout prevents threads from starving and never has a chance to lock and execute code public static final long timeout = 10000;//ms // Lock holding timeout prevents threads from executing indefinitely after locking, so that locks cannot be released public static final long expireMsecs = 10000;// ms public String lockKey = "orderLockKey"; public Jedis jedis; private static volatile JedisPool jedisPool; public RedisOrderLock(String lockKey) { this.lockKey = lockKey; } /** * Initialize redis * @return */ public Jedis getInstance() { if(jedisPool == null) { synchronized(RedisOrderLock.class) { if(jedisPool == null) { JedisPoolConfig config = new JedisPoolConfig(); config.setMaxIdle(100); jedisPool = new JedisPool(config,"localhost",6379, 3000,"test"); } } } return jedisPool.getResource(); } /** * Thread-Safe Business Logic Processing */ @Override public boolean isSyn(OrderLockBiz<T> orderBiz) { jedis = this.getInstance(); try { // Acquisition Lock if(acquire(jedis)){ // Execute order creation logic orderBiz.createOrder(); }else{ BizLogger.info("waiting other thread creating"); } } catch (Exception e) { BizLogger.error(e,"acquire lock failre"); }finally{ // Unlock this.releaseLock(jedis); } return false; } /** * accqure lock * @param jedis * @return * @throws InterruptedException */ public synchronized boolean acquire(Jedis jedis){ boolean locked = false; while(timeout > 0){ long expires = System.currentTimeMillis() + expireMsecs + 1; // The lock expires after 10 seconds String expiresStr = String.valueOf(expires); // Acquisition Lock if(jedis.setnx(lockKey, expiresStr) == 1){ locked = true; return locked; } // No lock was acquired String oldValue = jedis.get(lockKey); // If the expireMsecs (10 seconds) lock expires, the if judgment cannot be entered, if the lock expires if(oldValue != null && Long.parseLong(oldValue) < System.currentTimeMillis()){ // If the lock timeout is reset String oldValue_ = jedis.getSet(lockKey, expiresStr); // The same value indicates that the operation of the same thread succeeds in obtaining the lock. if(Long.valueOf(oldValue_) == Long.valueOf(oldValue)){ locked = true; }else{ // Preemptive acquisition of locks by other threads locked = false; } } // The lock has not timed out. Keep waiting return false; } } /** * Release lock * @param jedis */ public synchronized void releaseLock(Jedis jedis){ try { long current = System.currentTimeMillis(); // Avoid deleting locks that are not self-acquired if (current < Long.valueOf(jedis.get(lockKey))) jedis.del(lockKey); } catch (Exception e) { e.printStackTrace(); }finally{ // Place the used connection in the connection pool sink for other threads to call jedisPool.returnResource(jedis); } } }