Zookeeper典型应用场景 分布式锁的实现(五)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_34125999/article/details/100054250

1、分布式锁的概述

可以保证在分布式部署的应用集群中,同一个方法在同一时间只能被一台机器-上的一个线程执行。
避免死锁
获取锁和释放锁的性能要好

2、NoSafeDemo

public class NoSafe {

    private static CountDownLatch countDownLatch = new CountDownLatch(1);

    private static Integer number = 0;

    public static void main(String[] args) {
        for (int i = 1; i <= 50; i++) {
            new Thread(() -> {
                for (int j = 1; j <= 100; j++) {
                    number++;
                }
                System.out.println(Thread.currentThread().getName() + "->" + number);
            }, "t" + i).start();
        }

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

在这里插入图片描述

3、zookeeper实现分布式锁

大体思路:在锁的根节点下创建临时有序节点。获取锁的根节点下所有节点,如果当前节点是根节点的头节点,那么获取成功。如果不是,监听当前节点的前一个节点,监听事件为删除事件。

3.1、zkLock

public interface ZkLock {
    //加锁
    void lock();
    //解锁
    void unlock();
}

3.2、AbstractZkLock

public abstract class AbstractZkLock implements ZkLock {

    public void lock() {
        if (tryLock()) {
            System.out.println(Thread.currentThread().getName()+"获取该锁,执行任务");
        } else {
            //等待
            watiLock();
            //重新获取该锁
            lock();
        }
    }

    protected abstract boolean tryLock();


    public abstract void watiLock();
}

3.3、ZKLockImpl

public class ZKLockImpl extends AbstractZkLock {

    //等待
    private CountDownLatch countDownLatch = null;
    //前一个节点路径
    private String beforeNodePath;
    //当前节点路径
    private String currentNodePath;
    //zkClient
    private ZkClient zkClient = new ZkClient(ZkCommonConsts.ZOOKEEPER_ADDRESS, Integer.MAX_VALUE);

    private String BASE_PATH = "/lock";

    private String LOCK_PATH = "/lock";

    /**
     * 功能描述  尝试获取锁
     *
     * @date 2019/8/24
     */
    @Override
    protected boolean tryLock() {
        //1、判读BASE_PATH是否存在,如果不存在则建立
        if (!zkClient.exists(BASE_PATH)) {
            zkClient.createPersistent(BASE_PATH);
        }
        //2、排号
        if (StringUtils.isBlank(currentNodePath)) {
            currentNodePath = zkClient.createEphemeralSequential(BASE_PATH + LOCK_PATH, "lock");
        }
        //2、获取所有临时节点
        List<String> childrens = zkClient.getChildren(BASE_PATH);
        //3、如果当前锁的路径是列表头,认为得到了锁
        if (currentNodePath.equals(BASE_PATH + "/" + childrens.get(0))) {
            return true;
        }
        //4、如果当前节点不是排名第一,那么记住它前面的值
        else {
            int index = -1;
            for (int i = 0; i < childrens.size(); i++) {
                if (currentNodePath.equals(BASE_PATH + "/" + childrens.get(i))) {
                    index = i;
                    break;
                }
            }
            beforeNodePath = BASE_PATH + "/" + childrens.get(index - 1);
            return false;
        }
    }

    /**
     * 功能描述 等待锁
     *
     * @author Zrs
     * @date 2019/8/24
     */
    @Override
    public void watiLock() {
        //1、创建一个监听器,监听delete事件
        IZkDataListener listener = new IZkDataListener() {
            @Override
            public void handleDataChange(String s, Object o) throws Exception {
            }

            @Override
            public void handleDataDeleted(String s) throws Exception {
                if (countDownLatch != null) {
                    countDownLatch.countDown();
                }
            }
        };
        //2、在前一个节点添加wathch事件
        zkClient.subscribeDataChanges(beforeNodePath, listener);
        //3、判读前一个节点是否存在,如果存在那么就等待
        if (zkClient.exists(beforeNodePath)) {
            countDownLatch = new CountDownLatch(1);
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //4、清除监听事件
        zkClient.unsubscribeDataChanges(beforeNodePath, listener);
    }

    /***
     *功能描述 解锁
     */
    @Override
    public void unlock() {
        //删除当前节点
        zkClient.delete(currentNodePath);
        //关闭客户端
        zkClient.close();
    }
}

3.4、测试

public class TestLock {

    private static CountDownLatch countDownLatch = new CountDownLatch(1);

    private static Integer number = 0;

    public static void main(String[] args) {
        for (int i = 1; i <= 50; i++) {
            new Thread(() -> {
                ZkLock lock = new ZKLockImpl();
                try {
                    lock.lock();
                    for (int j = 1; j <= 100; j++) {
                        number++;
                    }
                    System.out.println(Thread.currentThread().getName() + "->" + number);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }, "t" + i).start();
        }

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

3.5、结果
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_34125999/article/details/100054250