implementation of distributed lock based on Redis
backgroundAccording to Redis's SETNX command, only one client can get the lock; the Redissonlock distributed lock implementation uses the LUA script, which provides a method that does not apply to script implementations;
Basic ImplementationUse the Redis setnx command, plus an expiration time to prevent deadlocks: No reentry is supported, wait is not supported, and if the time of the call unlock is >leasetime, the lock obtained after the deletion is deleted;
Import Redis.clients.jedis.Jedis;
Import Redis.clients.jedis.JedisPool;
Import Java.util.concurrent.TimeUnit;
/** * Created by Jinshuan.li on 2016/12/1.
* * Public class Defaultdistrlock implements Distrlock {private static jedispool Jedispool = null;
private static Long default_lease_time = 30000L;
Private String Lockkey;
Public Defaultdistrlock (String lockkey) {this.lockkey = Lockkey; /** * Initialization * * @param jedispool/public static void init (Jedispool jedispool) {De
Faultdistrlock.jedispool = Jedispool;
@Override public boolean Trylock () {Jedis Jedis = null;
try {Jedis = Jedispool.getresource ();
Long setnx = jedis.setnx (Lockkey, String.valueof (Thread.CurrentThread (). GetId ()));
if (Setnx.equals (1L)) {Jedis.pexpire (Lockkey, default_lease_time);
return true; }} finally {ClosejediS (Jedis);
return false;
@Override public boolean Trylock (long waittime, long leasetime, Timeunit unit) {Jedis = null;
try {Jedis = Jedispool.getresource ();
Long setnx = jedis.setnx (Lockkey, String.valueof (Thread.CurrentThread (). GetId ()));
if (Setnx.equals (1L)) {Long Tomillis = Unit.tomillis (leasetime);
Jedis.pexpire (Lockkey, Tomillis);
return true;
}} finally {Closejedis (Jedis);
return false;
@Override public void Unlock () {Jedis Jedis = null;
try {Jedis = Jedispool.getresource ();
Jedis.del (Lockkey);
finally {Closejedis (Jedis);
} private void Closejedis (Jedis Jedis) {if (Jedis!= null) {jedis.close ();
}
}
}
a reentrant distributed lock implementationIdea: After the setnx fails, see if its value is a specific value, and if so, you can continue to acquire the lock. At the same time add Holdcount record the number of locks, unlock when the corresponding processing disadvantages: Not to wait, each competition lock need to access Redis
/** * Created by Jinshuan.li on 2016/12/1.
* * Public class Defaultdistrlock implements Distrlock {private static jedispool Jedispool = null;
private static Long default_lease_time = 30000L;
Private String Lockkey;
Private String uuid = Uuid.randomuuid (). toString ();
Private Atomicinteger Holdcount = new Atomicinteger (0);
Public Defaultdistrlock (String lockkey) {this.lockkey = Lockkey; /** * Initialization * * @param jedispool/public static void init (Jedispool jedispool) {De
Faultdistrlock.jedispool = Jedispool;
@Override public boolean Trylock () {Jedis Jedis = null;
try {String Setvalue=uuid + thread.currentthread (). GetId ();
Jedis = Jedispool.getresource ();
Long setnx = jedis.setnx (Lockkey, SetValue);
if (Setnx.equals (1L)) {Jedis.pexpire (Lockkey, default_lease_time);
Holdcount.incrementandget (); return true;
String value = Jedis.get (Lockkey); if (stringutils.equals (uuid + thread.currentthread (). GetId (), value)) {//Can be reentrant holdcount.i
Ncrementandget ();
return true;
}} finally {Closejedis (Jedis);
///No Lock set to 0 Holdcount = new Atomicinteger (0);
return false;
@Override public boolean Trylock (long waittime, long leasetime, Timeunit unit) {Jedis = null;
try {String Setvalue=uuid + thread.currentthread (). GetId ();
Jedis = Jedispool.getresource ();
Long setnx = jedis.setnx (Lockkey, SetValue);
if (Setnx.equals (1L)) {Long Tomillis = Unit.tomillis (leasetime);
Jedis.pexpire (Lockkey, Tomillis);
Holdcount.incrementandget ();
return true; } String value = jedis.geT (Lockkey); if (stringutils.equals (uuid + thread.currentthread (). GetId (), value)) {//Can be reentrant holdcount.i
Ncrementandget ();
return true;
}} finally {Closejedis (Jedis);
} Holdcount = new Atomicinteger (0);
return false;
@Override public void Unlock () {final int countvalue = Holdcount.decrementandget ();
if (CountValue < 0) {throw new IllegalStateException ("This thread does don't get lock");
} if (CountValue = = 0) {Jedis Jedis = null;
try {Jedis = Jedispool.getresource ();
Jedis.del (Lockkey);
finally {Closejedis (Jedis);
}} private void Closejedis (Jedis Jedis) {if (Jedis!= null) {jedis.close ();
}
}
}
support for reentrant, waiting distributed locksIdea: Before accessing Redis, get a lock on the local JVM with a innerlock, because if you're competing in a local thread, you're more competitive in a distributed environment than the other threads; therefore, concurrent access to the Redis is only relevant to the number of machines; waiting implementations, Through the Redis pub, sub implementation, while using Countdownlatch to wait, in the notification of the sub countdown to activate the waiting thread, and finally try to get the lock;
Import Org.apache.commons.lang.StringUtils;
Import Redis.clients.jedis.Jedis;
Import Redis.clients.jedis.JedisPool;
Import Redis.clients.jedis.JedisPubSub;
Import Java.util.UUID;
Import Java.util.concurrent.CountDownLatch;
Import Java.util.concurrent.ExecutorService;
Import java.util.concurrent.Executors;
Import Java.util.concurrent.TimeUnit;
Import Java.util.concurrent.atomic.AtomicInteger;
Import Java.util.concurrent.locks.Lock;
Import Java.util.concurrent.locks.ReentrantLock;
/** * Created by Jinshuan.li on 2016/12/1.
* * Public class Defaultdistrlock implements Distrlock {private static jedispool Jedispool = null; private static Long default_lease_time = 30000L;
Default lease time private static Executorservice Executorservice = Executors.newfixedthreadpool (5);
Private String Lockkey;
Private String uuid = Uuid.randomuuid (). toString ();
Private Lock Innerlock = new Reentrantlock ();
Private Countdownlatch Countdownlatch = new Countdownlatch (1); Jedispubsub PubSub = new Jedispubsub () {@Override public void OnMessage (string channel, String message) {
if (Stringutils.equals ("DELETE", message)) {//has key expired this.unsubscribe (); @Override public void Onunsubscribe (String channel, int subscribedchannels) {//c
Ountdown to make the waiting thread inside the end Countdownlatch.countdown ();
}
};
Private Atomicinteger Holdcount = new Atomicinteger (0);
Private long firstacciretime = 0;
Private long leasetime = Default_lease_time;
Public Defaultdistrlock (String lockkey) {this.lockkey = Lockkey; /** * Initialization * * @param jedispool/public static void init (Jedispool jedispool) {D
Efaultdistrlock.jedispool = Jedispool;
@Override public boolean Trylock () {Boolean getlocallock = false;
Jedis Jedis = null;
try {//First win internal competition Getlocallock = Innerlock.trylock ();
if (!getlocallock) {return false;
} Jedis = Jedispool.getresource ();
Boolean remotelock = Getremotelock (Jedis, Default_lease_time, timeunit.milliseconds);
if (Remotelock) {return true;
}} finally {Closejedis (Jedis);
///No Lock set to 0 Holdcount = new Atomicinteger (0);
return false;
@Override public boolean Trylock (long waittime, long leasetime, Timeunit unit) {Jedis = null;
Boolean getlocallock = false;
Long Waittimemillis = Unit.tomillis (waittime);
try {//First win internal competition long StartTime = System.currenttimemillis ();
Getlocallock = Innerlock.trylock (waittime, timeunit.milliseconds);
if (!getlocallock) {return false;
} Jedis = Jedispool.getresource (); Attempt to acquire lock Boolean remotelock = Getremotelock (Jedis, leasetime, unit);
if (Remotelock) {return true; ///Based on wait time constant try while (true) {long lastwaittime = Waittimemillis-(system.curren Ttimemillis ()-starttime);
can also wait for the time if (lastwaittime <= 0) {break;
Long Pttl = Jedis.pttl (Lockkey);
if (null = = Pttl) {break; Long Shouldwaittime = Pttl < lastwaittime?
Pttl:lastwaittime; if (shouldwaittime!= 0) {if (Countdownlatch.getcount ()!= 1) {COUNTDOWNLATC h = new Countdownlatch (1);//re-assignment} subscribedelete (); Subscribe to Countdownlatch.await (Shouldwaittime, timeunit.milliseconds); Waiting So long} Lastwaittime = WaitTimemillis-(System.currenttimemillis ()-starttime); Recalculate time if (Lastwaittime > 0) {//There is still time to try to acquire the lock Remotelock = ge
Tremotelock (Jedis, leasetime, unit);
if (Remotelock) {return true;
} else {break;
A catch (Interruptedexception e) {e.printstacktrace ());
finally {Closejedis (Jedis);
} Holdcount = new Atomicinteger (0);
return false;
Private Jedispubsub Subscribedelete () {if (pubsub.issubscribed ()) {return pubSub; Executorservice.submit (New Runnable () {@Override public void run () {Je
Dis jedis1 = null;
try {jedis1 = Jedispool.getresource (); Jedis1.subscribe (PubSub, Lockkey + "-channeL ");
finally {Closejedis (JEDIS1);
}
}
});
return pubSub;
@Override public void Unlock () {innerlock.unlock ();
if (Holdcount.get () > 0) {//the final int countvalue = Holdcount.decrementandget () is deleted at this time;
if (countvalue!= 0) {return; The direct deletion here will have a problem consider a situation: Unlock time has passed Leasetime,key because the timeout expires, key by other threads to get a direct deletion of the object does not belong to the key Delete if (System.
Currenttimemillis ()-Firstacciretime >= leasetime) {//If the current time is greater than the lease time does not process return;
} Jedis Jedis = null;
try {Jedis = Jedispool.getresource ();
Jedis.del (Lockkey); Jedis.publish (Lockkey + "-channel", "DELETE");
Send a message} finally {Closejedis (Jedis); }} private void Closejedis (Jedis Jedis) {
if (Jedis!= null) {jedis.close ();
/** * Acquire remote lock * * @param Jedis * @param leasetime * @param unit * @return * * Private Boolean Getremotelock (Jedis Jedis, long leasetime, Timeunit unit) {String SetValue = uuid + THREAD.C
Urrentthread (). GetId ();
Long setnx = jedis.setnx (Lockkey, SetValue);
if (Setnx.equals (1L)) {Long Tomillis = Unit.tomillis (leasetime);
Jedis.pexpire (Lockkey, Tomillis);
Firstacciretime = System.currenttimemillis ();
This.leasetime = Leasetime;
Holdcount.incrementandget ();
return true;
String value = Jedis.get (Lockkey);
if (Stringutils.equals (SetValue, value)) {//Can be reentrant holdcount.incrementandget ();
return true;
return false;
}
}