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

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、结果
在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

响彻天堂丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值