To understand distributed locks, let’s first understand what distributed locks are.

Intra-process lock: allows threads to execute methods or blocks of code in a specified order, keeping thread atoms alive

Different processes: Locking at the JVM level does not work, so you can use a component from a third party to acquire the lock. If the lock is not acquired, the thread that you want to run will be blocked.

Scenario 1:

A: Bank deposit 200 read 200, deposit 100, get 200+100=300 B: read 200 from the same account, take out 100, get 200-100 = 100 the final account should be 200 unchanged, A get 300, B get 200, a-B data are inconsistentCopy the code

So we can use the distributed lock, let A, B two processes in order, so that B read the account to 300, and then sequence the final data is consistent.

Scenario 2:

At present, there are A bunch of tasks in the task pool, and both A and B need to obtain tasks from the task pool. When the task is obtained, the obtained task is deleted from the task pool. If there is no synchronization, A and B may obtain the same taskCopy the code

We can use distributed locks to ensure that the retrieval tasks are performed sequentially.

Why can ZK implement distributed locking

What is Zookeeper?

In fact, ZooKeeper can be understood as a file structure similar to Liunx. The core data structure of ZooKeeper is node node, which can store a small amount of data.

The diagram below:

Zk nodes have four properties

Node creation mode Chinese interpretation instructions
PERSISTENT Persistent node Znode will not be deleted automatically, even if the client is disconnected
PERSISTENT_SEQUENTIAL Persistent order node Znode is not automatically deleted and the name is automatically incremented
EPHEMERAL Temporary node Znode is automatically deleted and disconnected from the client
EPHEMERAL_SEQUENTIAL Temporary ordered node Znode is automatically deleted and its name is automatically incremented

When you create a node, you can specify the creation mode. When you specify the node of EPHEMERAL_SEQUENTIAL, an ordered node is generated. When you acquire the lock, you can determine whether the current node is the smallest node. Zk will notify the client through watcher mechanism when the current node abandons the lock, so that the current node can obtain the lock smoothly. When unlocking, delete the current node.

How to use ZK to realize distributed lock

The previous theoretical knowledge, so we can use code to implement

package com.skrein.zk;

import lombok.extern.slf4j.Slf4j;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.serialize.SerializableSerializer;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/ * * *@Author: hujiansong
 * @Date: 2019/5/16 17:29
 * @since: 1.8 * /
@Slf4j
public class DistributedLock implements Lock {

    private static final String LOCK_PATH = "/LOCK";

    private static final String ZOOKEEPER_IP_PORT = "localhost:2181";

    private ZkClient client = new ZkClient(ZOOKEEPER_IP_PORT, 4000.4000.new SerializableSerializer());

    private String currentPath;

    private String lastPath;

    private CountDownLatch countDownLatch;

    public DistributedLock(a) {
        // When creating a lock, check whether the parent node exists
        if (!client.exists(LOCK_PATH)) {
            client.createPersistent(LOCK_PATH);
        }
    }

    public void lock(a) {
        if(! tryLock()) { waitForLock(); lock(); }else {
            log.info("Lock obtained successfully, currently locked node {}", currentPath); }}private void waitForLock(a) {

        / / to monitor lastPath
        IZkDataListener listener = new IZkDataListener() {
            public void handleDataChange(String s, Object o) throws Exception {}public void handleDataDeleted(String s) throws Exception {
                log.info("Front node deleted,path={}", s);
                if(countDownLatch! =null) {// The front node is deleted, notifying wake up to get the lockcountDownLatch.countDown(); }}};// Listen on the front node
        client.subscribeDataChanges(lastPath, listener);
        // Check whether the front node exists
        if (this.client.exists(lastPath)) {
            countDownLatch = new CountDownLatch(1);
            try {
                // If the front node exists, block and wait
                countDownLatch.await();
            } catch (InterruptedException e) {
                log.error("CountDownLatch await Exception", e); }}// Unbind the listener of the previous node because it has been deleted
        client.unsubscribeDataChanges(lastPath, listener);

    }

    public void lockInterruptibly(a) throws InterruptedException {}public boolean tryLock(a) {
        // Create a temporary ordered node
        if (currentPath == null) {
            currentPath = client.createEphemeralSequential(LOCK_PATH + "/"."lock");
            log.info("Current lock path {}", currentPath);
        }
        // Get all child nodes
        List<String> children = client.getChildren(LOCK_PATH);
        Collections.sort(children);
        // The current minimum is considered to have obtained the lock successfully and returns true
        if (currentPath.equals(LOCK_PATH + "/" + children.get(0))) {
            return true;
        }
        // If the first one is not, then get the path before currentPath
        int i = Collections.binarySearch(children, currentPath.substring(6));
        try {
            lastPath = LOCK_PATH + "/" + children.get(i - 1);
        } catch (Exception e) {
            System.out.println(currentPath + "" + children);
        }
        return false;
    }

    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    public void unlock(a) {
        // Delete the current node
        log.info("Release lock {}",currentPath);
        this.client.delete(currentPath);
    }

    public Condition newCondition(a) {
        return null; }}Copy the code

Distributed lock usage


package com.skrein.zk;

import lombok.extern.slf4j.Slf4j;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;

/ * * *@Author: hujiansong
 * @Date: 2019/5/16 * returned from@since: 1.8 * /
@Slf4j
public class OrderService implements Runnable {

    private static AtomicLong ATOMICLONG = new AtomicLong(1);

    private static CountDownLatch COUNTDOWNLATCH = new CountDownLatch(10);

    private Lock lock = new DistributedLock();

    public void run(a) {
        try {
            COUNTDOWNLATCH.await();
        } catch (InterruptedException e) {
            log.error("CountDownLatch error", e);
        }
        String orderCode = null;
        lock.lock();
        try {
            // do biz
            orderCode = getOrderCode();
        } finally{ lock.unlock(); }}public String getOrderCode(a) {
        Date now = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        return sdf.format(now) + ATOMICLONG.getAndIncrement();
    }

    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            // Iterate on the number of threads instantiated
            new Thread(new OrderService()).start();
            // Create a thread and decrement the counter by 1COUNTDOWNLATCH.countDown(); }}}Copy the code

Log printing:

The 2019-05-17 10:53:14, 542 INFO Thread 1 (com. Skrein. Zk. DistributedLock. TryLock: 87) - the current LOCK path/LOCK / 0000000150 2019-05-17 10:53:14, 542 INFO Thread - 3 (com. Skrein. Zk. DistributedLock. TryLock, 87) - the current LOCK path/LOCK / 0000000153 2019-05-17 10:53:14, 542 INFO Thread - 5 (com. Skrein. Zk. DistributedLock. TryLock, 87) - the current LOCK path/LOCK / 0000000151 2019-05-17 10:53:14, 543 INFO Thread - 9 (com. Skrein. Zk. DistributedLock. TryLock, 87) - the current LOCK path/LOCK / 0000000159 2019-05-17 10:53:14, 543 INFO Thread - 15 (com. Skrein. Zk. DistributedLock. TryLock, 87) - the current LOCK path/LOCK / 0000000154 2019-05-17 10:53:14, 543 INFO Thread - 13 (com. Skrein. Zk. DistributedLock. TryLock, 87) - the current LOCK path/LOCK / 0000000156 2019-05-17 10:53:14, 543 INFO Thread - 7 (com. Skrein. Zk. DistributedLock. TryLock, 87) - the current LOCK path/LOCK / 0000000155 2019-05-17 10:53:14, 544 INFO Thread - 17 (com. Skrein. Zk. DistributedLock. TryLock, 87) - the current LOCK path/LOCK / 0000000157 2019-05-17 10:53:14, 543 INFO Thread - 11 (com. Skrein. Zk. DistributedLock. TryLock, 87) - the current LOCK path/LOCK / 0000000152 2019-05-17 10:53:14, 544 INFO Thread - 19 (com. Skrein. Zk. DistributedLock. TryLock, 87) - the current LOCK path/LOCK / 0000000158 2019-05-17 10:53:14, 547 INFO Thread - 1 (com. Skrein. Zk. DistributedLock. Lock: 46) - acquiring a lock is successful, the current lock the lock/node / 0000000150 2019-05-17 10:53:14, 560 INFO Thread - 1 (com. Skrein. Zk. DistributedLock. Unlock, 111) - releases the LOCK/LOCK / 0000000150 2019-05-17 10:53:14, 609 INFO Thread - 5 (com. Skrein. Zk. DistributedLock. Lock: 46) - acquiring a lock is successful, the current lock the lock/node / 0000000151 2019-05-17 10:53:14, 609 INFO Thread - 5 (com. Skrein. Zk. DistributedLock. Unlock, 111) - releases the LOCK/LOCK / 0000000151 2019-05-17 10:53:14, 643 INFO Thread - 11 (com. Skrein. Zk. DistributedLock. Lock: 46) - acquiring a lock is successful, the current lock the lock/node / 0000000152 2019-05-17 10:53:14, 644 INFO Thread - 11 (com. Skrein. Zk. DistributedLock. Unlock, 111) - releases the LOCK/LOCK / 0000000152 2019-05-17 10:53:14, 673 INFO Thread - 3 (com. Skrein. Zk. DistributedLock. Lock: 46) - acquiring a lock is successful, the current lock the lock/node / 0000000153 2019-05-17 10:53:14, 673 INFO Thread - 3 (com. Skrein. Zk. DistributedLock. Unlock, 111) - releases the LOCK/LOCK / 0000000153 2019-05-17 10:53:14, 724 INFO Thread - 15 (com. Skrein. Zk. DistributedLock. Lock: 46) - acquiring a lock is successful, the current lock the lock/node / 0000000154 2019-05-17 10:53:14, 724 INFO Thread - 15 (com. Skrein. Zk. DistributedLock. Unlock, 111) - releases the LOCK/LOCK / 0000000154 2019-05-17 10:53:14, 759 INFO Thread - 7 (com. Skrein. Zk. DistributedLock. Lock: 46) - acquiring a lock is successful, the current lock the lock/node / 0000000155 2019-05-17 10:53:14, 759 INFO Thread - 7 (com. Skrein. Zk. DistributedLock. Unlock, 111) - releases the LOCK/LOCK / 0000000155 2019-05-17 10:53:14, 800 INFO Thread - 13 (com. Skrein. Zk. DistributedLock. Lock: 46) - acquiring a lock is successful, the current lock the lock/node / 0000000156 2019-05-17 10:53:14, 800 INFO Thread - 13 (com. Skrein. Zk. DistributedLock. Unlock, 111) - releases the LOCK/LOCK / 0000000156 2019-05-17 10:53:14, 833 INFO Thread - 17 (com. Skrein. Zk. DistributedLock. Lock: 46) - acquiring a lock is successful, the current lock the lock/node / 0000000157 2019-05-17 10:53:14, 833 INFO Thread - 17 (com. Skrein. Zk. DistributedLock. Unlock, 111) - releases the LOCK/LOCK / 0000000157 2019-05-17 10:53:14, 872 INFO Thread - 19 (com. Skrein. Zk. DistributedLock. Lock: 46) - acquiring a lock is successful, the current lock the lock/node / 0000000158 2019-05-17 10:53:14, 872 INFO Thread - 19 (com. Skrein. Zk. DistributedLock. Unlock, 111) - releases the LOCK/LOCK / 0000000158 2019-05-17 10:53:14, 906 INFO Thread - 9 (com. Skrein. Zk. DistributedLock. Lock: 46) - acquiring a lock is successful, the current lock the lock/node / 0000000159 2019-05-17 10:53:14, 906 INFO Thread - 9 (com. Skrein. Zk. DistributedLock. Unlock, 111) - releases the LOCK/LOCK / 0000000159Copy the code

You can see that 10 threads, in turn, acquire and unbind locks.

Why can Redis implement distributed locks

Redis SetNx returns null if the key exists

The expired property of the key is needed to ensure that the lock is available if the thread that acquired the lock hangs and subsequent threads can acquire the lock correctly

Since it is a distributed lock, a requestId is required to ensure which request acquired the lock, so value can be the ID that identifies the client

If the setting is successful, the lock is successfully obtained. If the setting fails, you need to poll whether the key exists or TTL (time to Live) of the key to obtain the lock again until the lock is successfully obtained.

Unlock process: Get (key) == requestId? Del (key) Checks whether the result returned by the key is the current client. If so, delete the key

For those of you dealing with high concurrency, you can see that this operation is not atomic. Get (key) == requestId. The lock is discarded after the current thread executes services for a long time, and the lock is discarded after the key expires. The other thread acquires the lock and then executes del(key) to remove the lock from the other thread.

Fortunately, Redis provides us with lua scripts that combine multiple operations into one atomic operation. Therefore, the lua script is used to unlock the device.

Redis implements a distributed locking process

@Slf4j
public class RedisDistributedLock {

    private Jedis client = new Jedis("localhost");

    private static final String REDIS_LOCK_KEY = "DISTRIBUTE::LOCK::";

    /** * Lock expiration time ms */
    private static final Long LOCK_EXPIRED_TIME = 60 * 1000L;


    /** * Request lock ID, identifying which client acquired the lock */
    private String reqLockId;


    public RedisDistributedLock(a) {
        // Use the uuid + current timestamp to generate the lock ID
        this.reqLockId = UUID.randomUUID().toString() + System.currentTimeMillis();
    }

    public void lock(String key) throws InterruptedException {
        if(! tryLock(key)) { waitForLock(key); lock(key); }else {
            log.info("Lock obtained successfully!); }}private void waitForLock(final String key) throws InterruptedException {
        // Start a thread to poll redis for key expiration
        for(; ;) { String lockKey = REDIS_LOCK_KEY + key; Long ttl = client.ttl(lockKey);// The key does not exist or has expired, then breaks to retrieve the lock again
            if(! client.exists(lockKey) || ttl.intValue() <=0) {
                log.info("The current lock has expired at :{}", ttl);
                break; }}}// The lock is acquired by setnx
    public boolean tryLock(String key) {
        String tryLock = client.set(REDIS_LOCK_KEY + key, reqLockId, SetParams.setParams().nx().px(LOCK_EXPIRED_TIME));
        returntryLock ! =null && tryLock.equals("OK");
    }


    // Unlock using the eval Lua script
    public boolean unlock(String key) {
        // Check whether the value of the key is RequestId. If so, delete the key
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object eval = client.eval(script, Collections.singletonList(REDIS_LOCK_KEY + key), Collections.singletonList(reqLockId));
        return "OK".equals(eval);
    }


    public static void main(String[] args) throws InterruptedException {
        String orderId = "123";
        for (int i = 0; i < 10; i++) {
            // Start 10 threads to simulate concurrency
            new Thread(newOrderService(orderId)).start(); }}static class OrderService implements Runnable {

        private String orderId;

        private RedisDistributedLock lock = new RedisDistributedLock();

        public OrderService(String orderId) {
            this.orderId = orderId;
        }

        public void run(a) {
            try {
                lock.lock(orderId);
                log.info("do biz....");
                Thread.sleep(5000);
                log.info("do biz finished!");
            } catch (InterruptedException e) {
                log.error("lock InterruptedException", e);
            } finally{ lock.unlock(orderId); }}}}Copy the code
"C:\Program Files\Java\jdk1.8.0_161\bin\java.exe" -javaagent:C:\Users\hujiansong\AppData\Local\JetBrains\Toolbox\apps\IDEA-U\ch-0\191.7141.44\lib\idea_rt.jar=63968:C:\Users\hujiansong\AppData\Local\JetBrains\Toolbox\apps\IDEA-U\ch-0\191.7141.44\bin -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_161\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\rt.jar;C:\Users\hujiansong\Desktop\tmp\distributed-lock\target\classes;F:\DevTool\apache-maven-3.5.3\repo\org\projectlombok\lombok\1.18.6\lombok-1.18.6.jar;F:\DevTool\apache-maven-3.5.3\repo\org\slf4j\slf4j-api\1.7.26\slf4j-api-1.7.26.jar;F:\DevTool\apache-maven-3.5.3\repo\log4j\log4j\1.2.17\log4j-1.2.17.jar;F:\DevTool\apache-maven-3.5.3\repo\com\101tec\zkclient\0.10\zkclient-0.10.jar;F:\DevTool\apache-maven-3.5.3\repo\org\apache\zookeeper\zookeeper\3.4.8\zookeeper-3.4.8.jar;F:\DevTool\apache-maven-3.5.3\repo\org\slf4j\slf4j-log4j12\1.6.1\slf4j-log4j12-1.6.1.jar;F:\DevTool\apache-maven-3.5.3\repo\jline\jline\0.9.94\jline-0.9.94.jar;F:\DevTool\apache-maven-3.5.3\repo\io\netty\netty\3.7.0.Final\netty-3.7.0.Final.jar;F:\DevTool\apache-maven-3.5.3\repo\redis\clients\jedis\3.0.1\jedis-3.0.1.jar;F:\DevTool\apache-maven-3.5.3\repo\org\apache\commons\commons-pool2\2.4.3\commons-pool2-2.4.3.jar" com.skrein.redis.RedisDistributedLock
2019-05-17 17:11:14,007  INFO Thread-7 (com.skrein.redis.RedisDistributedLock.lock:45) - 获取锁成功!
2019-05-17 17:11:14,008  INFO Thread-7 (com.skrein.redis.RedisDistributedLock.run:96) - do biz....
2019-05-17 17:11:19,009  INFO Thread-7 (com.skrein.redis.RedisDistributedLock.run:98) - do biz finished!
2019-05-17 17:11:19,012  INFO Thread-8 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:-2
2019-05-17 17:11:19,012  INFO Thread-2 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:-2
2019-05-17 17:11:19,013  INFO Thread-8 (com.skrein.redis.RedisDistributedLock.lock:45) - 获取锁成功!
2019-05-17 17:11:19,014  INFO Thread-8 (com.skrein.redis.RedisDistributedLock.run:96) - do biz....
2019-05-17 17:11:19,012  INFO Thread-4 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:19,012  INFO Thread-3 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:-2
2019-05-17 17:11:19,012  INFO Thread-9 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:-2
2019-05-17 17:11:19,012  INFO Thread-5 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:19,012  INFO Thread-0 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:19,012  INFO Thread-1 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:19,012  INFO Thread-6 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:24,014  INFO Thread-8 (com.skrein.redis.RedisDistributedLock.run:98) - do biz finished!
2019-05-17 17:11:24,015  INFO Thread-6 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:24,016  INFO Thread-3 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:-2
2019-05-17 17:11:24,016  INFO Thread-4 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:-2
2019-05-17 17:11:24,017  INFO Thread-6 (com.skrein.redis.RedisDistributedLock.lock:45) - 获取锁成功!
2019-05-17 17:11:24,017  INFO Thread-6 (com.skrein.redis.RedisDistributedLock.run:96) - do biz....
2019-05-17 17:11:24,016  INFO Thread-1 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:-2
2019-05-17 17:11:24,016  INFO Thread-2 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:-2
2019-05-17 17:11:24,016  INFO Thread-0 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:24,015  INFO Thread-5 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:24,017  INFO Thread-9 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:-2
2019-05-17 17:11:29,017  INFO Thread-6 (com.skrein.redis.RedisDistributedLock.run:98) - do biz finished!
2019-05-17 17:11:29,017  INFO Thread-1 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:29,018  INFO Thread-4 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:29,017  INFO Thread-0 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:29,018  INFO Thread-3 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:-2
2019-05-17 17:11:29,017  INFO Thread-9 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:29,018  INFO Thread-1 (com.skrein.redis.RedisDistributedLock.lock:45) - 获取锁成功!
2019-05-17 17:11:29,018  INFO Thread-1 (com.skrein.redis.RedisDistributedLock.run:96) - do biz....
2019-05-17 17:11:29,018  INFO Thread-5 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:-2
2019-05-17 17:11:29,018  INFO Thread-2 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:-2
2019-05-17 17:11:34,018  INFO Thread-1 (com.skrein.redis.RedisDistributedLock.run:98) - do biz finished!
2019-05-17 17:11:34,019  INFO Thread-4 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:34,019  INFO Thread-3 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:34,019  INFO Thread-0 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:34,020  INFO Thread-2 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:-2
2019-05-17 17:11:34,020  INFO Thread-9 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:-2
2019-05-17 17:11:34,020  INFO Thread-4 (com.skrein.redis.RedisDistributedLock.lock:45) - 获取锁成功!
2019-05-17 17:11:34,020  INFO Thread-4 (com.skrein.redis.RedisDistributedLock.run:96) - do biz....
2019-05-17 17:11:34,020  INFO Thread-5 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:-2
2019-05-17 17:11:39,020  INFO Thread-4 (com.skrein.redis.RedisDistributedLock.run:98) - do biz finished!
2019-05-17 17:11:39,020  INFO Thread-9 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:39,020  INFO Thread-2 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:-2
2019-05-17 17:11:39,021  INFO Thread-5 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:-2
2019-05-17 17:11:39,021  INFO Thread-9 (com.skrein.redis.RedisDistributedLock.lock:45) - 获取锁成功!
2019-05-17 17:11:39,021  INFO Thread-9 (com.skrein.redis.RedisDistributedLock.run:96) - do biz....
2019-05-17 17:11:39,020  INFO Thread-3 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:39,021  INFO Thread-0 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:-2
2019-05-17 17:11:44,021  INFO Thread-9 (com.skrein.redis.RedisDistributedLock.run:98) - do biz finished!
2019-05-17 17:11:44,021  INFO Thread-3 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:44,021  INFO Thread-0 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:44,022  INFO Thread-2 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:-2
2019-05-17 17:11:44,022  INFO Thread-5 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:-2
2019-05-17 17:11:44,022  INFO Thread-3 (com.skrein.redis.RedisDistributedLock.lock:45) - 获取锁成功!
2019-05-17 17:11:44,022  INFO Thread-3 (com.skrein.redis.RedisDistributedLock.run:96) - do biz....
2019-05-17 17:11:49,022  INFO Thread-3 (com.skrein.redis.RedisDistributedLock.run:98) - do biz finished!
2019-05-17 17:11:49,023  INFO Thread-0 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:49,023  INFO Thread-2 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:49,023  INFO Thread-5 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:49,023  INFO Thread-0 (com.skrein.redis.RedisDistributedLock.lock:45) - 获取锁成功!
2019-05-17 17:11:49,023  INFO Thread-0 (com.skrein.redis.RedisDistributedLock.run:96) - do biz....
2019-05-17 17:11:54,023  INFO Thread-0 (com.skrein.redis.RedisDistributedLock.run:98) - do biz finished!
2019-05-17 17:11:54,024  INFO Thread-5 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:54,024  INFO Thread-2 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:55
2019-05-17 17:11:54,024  INFO Thread-5 (com.skrein.redis.RedisDistributedLock.lock:45) - 获取锁成功!
2019-05-17 17:11:54,024  INFO Thread-5 (com.skrein.redis.RedisDistributedLock.run:96) - do biz....
2019-05-17 17:11:59,024  INFO Thread-5 (com.skrein.redis.RedisDistributedLock.run:98) - do biz finished!
2019-05-17 17:11:59,025  INFO Thread-2 (com.skrein.redis.RedisDistributedLock.waitForLock:56) - 当前锁已经过期过期时间为:-2
2019-05-17 17:11:59,026  INFO Thread-2 (com.skrein.redis.RedisDistributedLock.lock:45) - 获取锁成功!
2019-05-17 17:11:59,026  INFO Thread-2 (com.skrein.redis.RedisDistributedLock.run:96) - do biz....
2019-05-17 17:12:04,026  INFO Thread-2 (com.skrein.redis.RedisDistributedLock.run:98) - do biz finished!

Process finished with exit code 0

Copy the code

You can see that locks are acquired and then released.

conclusion

ZK implements distributed locking, mainly using ZK’s Node creation mode and notification mechanism.

Redis implements distributed, relies on SexNX commands, and unlocks lua scripts to ensure atomicity

The reliability of locks is as follows:

Reliability characteristic explain
Lock is incompatible At the same time, only one client can acquire the lock
NO deadlock When the client acquires the lock, if the client suddenly hangs, the next client acquires the lock correctly
Fault tolerance Redis is guaranteed to be reliable, that is, locks are available when most nodes in the Redis cluster are available
Release lock properly Client A does not release the lock on client B. The lock must be released by the lock holder

Here’s zK and Redis’ satisfaction with reliability:

ZK Redis
Lock is incompatible Yes, node is unique Meet, setNx
NO deadlock The session timeout mechanism is enabled Meet, expired
Fault tolerance Rely on the ZK cluster Rely on Redis clusters
Release lock properly Rely on the Wather mechanism Polling, implemented by code

Disclaimer: The above is my learning distributed lock demo example, not on the production. We are not responsible for any problems if we produce.

Code warehouse

The complete code has been put into my Github repository. Welcome star

The code address is as follows:

DistributedLock