Distributed Lock There are three general ways:
1. Database optimistic locking;
2. Redis-based distributed lock;
3. Based on ZooKeeper distributed lock.
This blog will introduce a third way, based on the realization Zookeeper distributed lock. Although the Internet has introduced a variety of Zookeeper distributed lock implemented blog, but they realize they have a variety of problems, in order to avoid fraught, this blog will detail how to implement the Zookeeper distributed lock properly.
Now use a simulation to achieve Zookeeper distributed lock, assuming that there are A, B, C three clients to access resources, call zookeeper get the lock. Three create a client / lock node under / locks node zookeeper, because the node is the uniqueness of the properties, only one person will be successfully created, and the remaining two creation fails, it will change into the monitor / locks node, if / locks erupted node / lock node changes, the other two can pick locks, whether this is good? Like this can lead to shock group effect. It is a trigger so that it will trigger a lot in a short time watcher of events, but only one client can get a lock. So this approach is not recommended.
There is a better approach is to use the ordered node zookeeper's characteristics, the basic idea:
1, create a temporary order of the nodes in the locker node in a distributed lock acquisition time, delete the temporary node when the lock is released.
2, the client calls createNode method to create a temporary order of nodes in the locks, and then call getChildren ( "locks") to acquire all the child nodes locks below, note this time without setting any Watcher.
3, after the clients obtain all the child nodes path, if we find the minimum number of child nodes that you have created, it considers the client to get a lock.
4, if the discovery node to create their own locks is not all child nodes of the smallest, indicating that he still did not get the lock, then the client needs to find the node is smaller than its own, then it calls the exist () method, while its register event listeners.
5, then, let this node is concerned about the deletion, the client's Watcher will receive appropriate notice, this time again to determine whether the node that you create are locks child nodes smallest number, if it is acquired to lock, if it is not repeat the above steps to continue to acquire smaller than a node and its registered listeners.
The following look at my code to achieve:
public class DistributedLock the implements Lock, Watcher { Private the ZooKeeper ZK = null ; Private String ROOT_LOCK = "/ Locks"; // definition of root Private String WAIT_LOCK; // a lock wait before Private String CURRENT_LOCK; // indicates the current lock // as blocking Private a CountDownLatch CountDownLatch; // public DistributedLock () { the try { ZK = new new the ZooKeeper ( "192.168.254.135:2181" , 4000, the this); // determines whether there is a root node Stat STAT = zk.exists (ROOT_LOCK, to false ); IF (STAT == null ) { // If create absent zk.create (ROOT_LOCK, "0" .getBytes (), ZooDefs .Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } the catch (IOException E) { e.printStackTrace (); } the catch (InterruptedException E) { e.printStackTrace (); } the catch (KeeperException E) { e.printStackTrace (); } } / ** * try to acquire locks * / @Override public Boolean tryLock () { the try { // Create a temporary ordered node CURRENT_LOCK = zk.create (ROOT_LOCK + "/", "0" .getBytes (), ZooDefs.Ids. OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); System.out.println (Thread.currentThread () getName (). + "->" + CURRENT_LOCK + ", attempt to lock contention" ); List <String> Childrens = zk.getChildren (ROOT_LOCK, to false ); // Get all the child nodes below the root node the SortedSet <String> = SortedSet new newTreeSet (); // define a set of sort for (String Children: Childrens) { // Sort sortedSet.add (ROOT_LOCK + "/" + Children); } String firstNode = sortedSet.first (); // get all current sub the smallest node node // remove nodes than I created smaller nodes, if not for the null SortedSet <String> lessThenMe = ((TreeSet <String> ) SortedSet) .headSet (CURRENT_LOCK); IF (CURRENT_LOCK.equals (firstNode )) { // by the current node and child node of the smallest node comparison, if they are equal, the lock is obtained indicates successful return to true ; } IF (! lessThenMe.isEmpty ()) { WAIT_LOCKlessThenMe.last = (); // get the current node is smaller than the last node is provided to WAIT_LOCK } } the catch (KeeperException E) { e.printStackTrace (); } the catch (InterruptedException E) { e.printStackTrace (); } return to false ; } @Override public void lock () { IF ( the this .tryLock ()) { // If successful lock is obtained . System.out.println (Thread.currentThread () getName ( ) + "->" + CURRENT_LOCK + "-> to obtain a lock successfully" ); return ; } The try { waitForLock (WAIT_LOCK); // do not get the lock, wait for a lock to continue } the catch (KeeperException E) { e.printStackTrace (); } the catch (InterruptedException E) { e.printStackTrace (); } } Private Boolean waitForLock (String prev) throws KeeperException, InterruptedException { // listening on a node of the current node registered event, here need to be addressed in the default watch events inside // here is the process inside the callback Please watch events we mentioned before trigger execution of final See most downlink codes Stat STAT = zk.exists (PREV, to true ); IF (STAT =!null ) { . System.out.println (Thread.currentThread () getName () + "-> Lock Wait" + ROOT_LOCK + "/" + prev + " release" ); CountDownLatch = new new a CountDownLatch (. 1 ); CountDownLatch.await ( ); // enters a wait, there needs // later TODO watcher trigger, also we need to determine the current node is not a minimum wait again System.out.println (Thread.currentThread () getName () + "-> get the lock success. " ); } return to true ; } @Override public void lockInterruptibly() throws InterruptedException { } @Override public Boolean tryLock ( Long Time, TimeUnit Unit) throws InterruptedException { return to false ; } @Override public void UNLOCK () { System.out.println (. Thread.currentThread () getName () + "-> release locks" + CURRENT_LOCK) ; the try { // -1 represents the first node deleted anyway say zk.delete (CURRENT_LOCK, -1 ); CURRENT_LOCK = null ; zk.close (); } the catch (InterruptedException E) { e.printStackTrace(); } catch (KeeperException e) { e.printStackTrace(); } } @Override public Condition newCondition() { return null; } @Override public void process(WatchedEvent event) { // 事件回调 countDownLatch.countDown(); if(this.countDownLatch!=null){ this.countDownLatch.countDown(); } } }
Code implements Lock, Watcher two interfaces. The main lock is used inside trylock method, attempt to acquire the lock. Then there watcher inside the callback processing method
Test code
public static void main( String[] args ) throws IOException { CountDownLatch countDownLatch=new CountDownLatch(10); for(int i=0;i<10;i++){ new Thread(()->{ try { countDownLatch.await(); DistributedLock distributedLock=new DistributedLock(); distributedLock.lock(); //获得锁 } catch (InterruptedException e) { e.printStackTrace(); } },"Thread-"+i).start(); countDownLatch.countDown(); } System.in.read(); }
operation result:
. 8-the Thread -> / Locks / 0000000040 , lock contention attempt the Thread -5 -> / Locks / 0,000,000,044 , lock contention attempt the Thread -9 -> / Locks / 0,000,000,041 , lock contention attempt the Thread -3 -> / Locks / 0,000,000,042 , try competition lock the Thread -7 -> / locks / 0,000,000,046 , lock contention attempt the Thread -1 -> / locks / 0,000,000,043 , lock contention attempt the Thread -0 -> / locks / 0,000,000,047 , lock contention attempt the Thread -4 -> / locks / 0,000,000,045 , lock contention attempt the Thread -2 -> / locks / 0000000049 , lock contention attempt the Thread -6 -> / locks / 0,000,000,048 , lock contention attempt the Thread -8 -> / locks / 0000000040-> obtained successfully lock the Thread -9-> wait lock / locks //locks / 0000000040 release Thread-5-> waiting for the lock / Locks // Locks / 0,000,000,043 release Thread-0-> waiting for the lock / Locks // Locks / 0,000,000,046 release Thread-1-> waiting for the lock / Locks // Locks / 0,000,000,042 release Thread -2-> waiting for the lock / locks // locks / 0,000,000,048 release Thread-7-> waiting for the lock / locks // locks / 0,000,000,045 release Thread-6-> waiting for the lock / locks // locks / 0,000,000,047 release Thread-3-> wait lock / locks // locks / 0000000041 release Thread-4-> waiting for the lock / locks // locks / release 0,000,000,044