zookeeper实现分布式锁

╄→尐↘猪︶ㄣ 提交于 2020-03-17 11:53:15

思考:分布式锁实现的逻辑

1.争抢锁,只有一个线程可以获得锁。

2.获得锁的线程挂了,产生死锁。解决:使用临时节点(伴随客户端的session,session删除时临时节点也会删除)。

3.获得锁的线程执行成功,释放锁。

4.上面两点,锁被删除或者释放,其他线程如何知道。

(1.主动轮询,弊端:延迟,压力 。 2.watch,解决延迟问题,弊端:压力)

5.sequence(序列节点) + watch:watch前一个,最小的获得锁,一旦最小的释放锁,只给watch他的那个发事件回调。

public class WatchCallBack   implements Watcher  ,AsyncCallback.StringCallback ,AsyncCallback.Children2Callback ,AsyncCallback.StatCallback {

        ZooKeeper zk ;
        String threadName;
        CountDownLatch cc = new CountDownLatch(1);
        String pathName;

        public String getPathName() {
            return pathName;
        }

        public void setPathName(String pathName) {
            this.pathName = pathName;
        }

        public String getThreadName() {
            return threadName;
        }

        public void setThreadName(String threadName) {
            this.threadName = threadName;
        }

        public ZooKeeper getZk() {
            return zk;
        }

        public void setZk(ZooKeeper zk) {
            this.zk = zk;
        }

        public void tryLock(){
            try {
                System.out.println(threadName + "create....");
                //10个线程并行去在/lock目录下创建临时有序的锁节点
                zk.create("/lock",threadName.getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL,this,"abc");
                //获得锁的线程去 -1,获得锁之前都阻塞
                cc.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public void unLock(){
            try {
                zk.delete(pathName,-1);
                System.out.println(threadName + "释放锁....");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (KeeperException e) {
                e.printStackTrace();
            }
        }

        /**
         * watch的回调方法
         * @param event 事件
         */
        @Override
        public void process(WatchedEvent event) {
            //如果第一个线程锁释放了,只有他后面的一个收到了回调事件
            //如果中间的某个挂了,也能造成他后边的收到这个通知,从而让他后边那个跟去watch他之前的
            switch (event.getType()) {
                case None:
                    break;
                case NodeCreated:
                    break;
                case NodeDeleted:
                    //从新获取锁节点的集合
                    zk.getChildren("/",false,this,"sdf");
                    break;
                case NodeDataChanged:
                    break;
                case NodeChildrenChanged:
                    break;
            }

        }

        /**
         *在/lock目录下创建临时有序的锁节点的回调
         */
        @Override
        public void processResult(int rc, String path, Object ctx, String name) {
            if(name != null ){
                System.out.println(threadName + "create node : " + name);
                pathName = name;
                //10个线程去获得各自锁节点之前的锁节点
                //此处不需要watch,因为不需要关注锁目录的变化,只是要关注他前面的锁节点的变化
                zk.getChildren("/", false, this, "sdf");
            }

        }

        /**
         * getChildren的回调
         */
        @Override
        public void processResult(int rc, String path, Object ctx, List<String> children, Stat stat) {

            //将自己能看到的锁节点排序
            Collections.sort(children);
            //获得自己在集合中的位置
            int i = children.indexOf(pathName.substring(1));
            //判断自己是不是第一个
            if(i == 0){
                System.out.println(threadName +"我是最小的锁节点....");
                try {
                    zk.setData("/",threadName.getBytes(),-1);
                    //countDown,让tryLock不在阻塞,自己获得锁
                    cc.countDown();
                } catch (KeeperException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else{
                //如果不是第一个,就给自己前面的一个添加一个watch,如果他发生变化,则继续走到这个方法里判断
                zk.exists("/"+children.get(i-1),this,this,"sdf");
            }

        }

        /**
         * 给自己前面一个添加个watch时的回调
         */
        @Override
        public void processResult(int rc, String path, Object ctx, Stat stat) {
        }
    }
public class TestLock {

        ZooKeeper zk;

        @Before
        public void conn (){
            zk  = ZKUtils.getZK();
        }

        @After
        public void close (){
            try {
                zk.close();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        @Test
        public void lock(){
            for (int i = 0; i < 10; i++) {
                new Thread(){
                    @Override
                    public void run() {
                        //每个线程都有独立的 WatchCallBack
                        WatchCallBack watchCallBack = new WatchCallBack();
                        watchCallBack.setZk(zk);
                        String threadName = Thread.currentThread().getName();
                        watchCallBack.setThreadName(threadName);

                        //每一个线程尝试抢锁
                        watchCallBack.tryLock();
                        //获得锁后执行业务
                        System.out.println(threadName + "获得锁,执行业务...");
                        //释放锁
                        watchCallBack.unLock();
                    }
                }.start();
            }

            //让主线程阻塞
            while(true){

            }
        }
    }

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!