zookeeper分布式锁


ZkLockService :定义一个公共接口


/**
 * zk分布式锁公共接口
 */
public interface ZkLockService {

    /**
     * 加锁接口
     *
     * @param path 抢锁路径
     * @param flag 抢锁线程自己的标志
     * @return
     */
    Boolean lock(String path, String flag);

    /**
     * 解锁接口
     *
     * @param path 抢锁路径
     * @param flag 抢锁线程自己的标志
     * @return
     */
    Boolean unlock(String path, String flag);

    /**
     * 判断当前节点是否已被抢 true表示被占有了
     *
     * @param path 节点路径
     * @return
     */
    Boolean exists(String path);
}

都去抢注册一个节点

这种方式可以将同步做成非阻塞式的,就是未获取到锁,就返回。当然也可以做成阻塞式。
NonBlockZk :实现公共接口。


import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * 非阻塞:抢占注册同一个节点
 */
public class NonBlockZk implements ZkLockService {

    private ZooKeeper zkClient;

    /**
     * 保证zk能脸上服务
     */
    private CountDownLatch countDownLatch = new CountDownLatch(1);

    /**
     * 测试用,用来测试累加
     */
    public static int TEST_NO1 = 0;
    /**
     * 各线程抢占的节点
     */
    public static String LOCK_PATH = "/lock";

    {
        init();
    }

    public void init(){
        try {
            zkClient = new ZooKeeper("127.0.0.1:2181", 5000, new Watcher() {

                @Override
                public void process(WatchedEvent watchedEvent) {
                    countDownLatch.countDown();
                }
            });
            countDownLatch.await();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public String getId() {
        return UUID.randomUUID().toString();
    }
    @Override
    public Boolean lock(String path, String flag) {
        try {
            if (!exists(path)) {
                //加锁失败,这里会抛异常
                zkClient.create(path, flag.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
                byte[] data = zkClient.getData(path, false, null);
                //判断是不是自己线程打的标记
                if (data != null && flag.equals(new String(data))) {
                    System.out.println(Thread.currentThread().getName() + "获取成功");
                    return true;
                }
            }
        } catch (KeeperException e) {
            return false;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }

	//这个flag的作用是,判断是不是当前线程删除的
    @Override
    public Boolean unlock(String path, String flag) {
        try {
            Stat stat = new Stat();
            byte[] data = zkClient.getData(path, false, stat);
            //当前线程释放锁时,判断是不是该线程打的标记
            if (data != null && flag.equals(new String(data))) {
                zkClient.delete(path, stat.getVersion());
                System.out.println(Thread.currentThread().getName() + "释放了锁");
                return true;
            }
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public Boolean exists(String path) {
        Stat exists = null;
        try {
            exists = zkClient.exists(path, false);
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return exists != null;
    }

    public Boolean close() {
        try {
            zkClient.close();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return true;
    }
}
import com.lq.fitness.zk.yuansheng.yongjiu.NonBlockZk;

/**
 * 定义一个测试业务类
 */
public class YeWu implements Runnable {
    private NonBlockZk lock = new NonBlockZk();

    @Override
    public void run() {
        //给当前线程生成一个id
        String id = lock.getId();
        try {
            //如果是if,没获取锁就直接退出,while表示直到获取锁为止
            while (!lock.lock(NonBlockZk.LOCK_PATH, id)) {
                /*System.out.println("获取锁失败:" + Thread.currentThread().getName());
                return;*/
            }
            NonBlockZk.TEST_NO1++;
            NonBlockZk.TEST_NO1++;
            NonBlockZk.TEST_NO1++;
            NonBlockZk.TEST_NO1++;
            NonBlockZk.TEST_NO1++;
            NonBlockZk.TEST_NO1++;
            NonBlockZk.TEST_NO1++;
            NonBlockZk.TEST_NO1++;
            NonBlockZk.TEST_NO1++;
            NonBlockZk.TEST_NO1++;
            System.out.println(NonBlockZk.TEST_NO1);
        } finally {
            //解锁
            lock.unlock(NonBlockZk.LOCK_PATH, id);
            lock.close();
        }
    }
}


/**
 * 启动测试
 */
public class Test {
    public static void main(String[] args) {
        //启动100个线程去对 NonBlockZk.TEST_NO 做++
        for (int i = 0; i < 10000; i++) {
            new Thread(new YeWu()).start();
        }
    }
}

基于在同一个路径下注册带序号的节点

ZkLock


import com.alibaba.druid.util.StringUtils;
import com.lq.fitness.zk.yuansheng.yongjiu.ZkLockService;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * zk分布式锁
 */
public class ZkLock implements ZkLockService {
    /**
     * 测试用,用来测试累加
     */
    public static int TEST_NO1 = 0;

    private ZooKeeper zkclient = null;

    /**
     * 所有锁都会注册在这个路径下
     */
    private String rootPath = "/seqLock";

    /**
     * 注册节点的全路径,-符号后面会自动加上序号
     */
    private String rootSeqPath = rootPath + "/seq-";

    /**
     * 保证zk能连接上,zk连接上后才释放
     */
    private CountDownLatch zkInitLatch = new CountDownLatch(1);

    /**
     * 这个对象是用来等待获取锁,获取锁后才会释放
     */
    private CountDownLatch lockLatch = new CountDownLatch(1);
    /**
     * 存放前一个节点
     */
    private String prePath;
    /**
     * 当前节点
     */
    private String myPath;
    /**
     * 获取锁是否成功
     */
    private Boolean lockRes = Boolean.FALSE;

    {
         init();
    }

    public void init() {
        try {
            zkclient = new ZooKeeper("127.0.0.1:2181", 15000, new Watcher() {

                @Override
                public void process(WatchedEvent watchedEvent) {
                    //如果客户端连接上服务端,就释放等待zkInitLatch
                    if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
                        zkInitLatch.countDown();
                    }
                    //如果监听的事件为NodeDeleted并且被删除的节点为前一个路径节点prePath
                    if (watchedEvent.getType() == Event.EventType.NodeDeleted && watchedEvent.getPath().equals(prePath)) {
                        lockLatch.countDown();
                        lockRes = true;
                    }
                }
            });
            zkInitLatch.await();
            exists(rootPath);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public Boolean lock(String path, String flag) {
        //创建对应的临时节点带序号
        try {
            myPath = zkclient.create(rootSeqPath, rootSeqPath.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            if (StringUtils.isEmpty(myPath)) {
                throw new RuntimeException("异常");
            }
            List<String> children = zkclient.getChildren(rootPath, false);
            if (children == null || children.isEmpty()) {
                throw new RuntimeException("异常");
            }

            if (children.size() == 1) {
                //表示可以直接加锁
                System.out.println(Thread.currentThread().getName()+"获取了锁" + myPath);
                return true;
            }else {
                Collections.sort(children);
                System.out.println(Thread.currentThread().getName()+"加锁时还有这么多节点:" + children + "我的节点:" + myPath);
                String myPathSeq = myPath.substring(myPath.lastIndexOf("/") + 1);
                int myIndex = children.indexOf(myPathSeq);
                if (myIndex == 0) {
                    return true;
                }
                prePath = rootPath + "/" +children.get(myIndex-1);
                try {
                    zkclient.getData(prePath, true, null);
                    System.out.println(Thread.currentThread().getName()+"监听了:" + prePath );
                } catch (KeeperException.NoNodeException e) {
                    //表示前面那个节点已经不存在了,可以直接上锁
                    return true;
                }
                //等待获取锁,如果获取了,会在watch函数中解除等待
                lockLatch.await();
            }
        } catch (KeeperException e) {
            e.printStackTrace();
            return lockRes;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return lockRes;
        }
        if (lockRes) {
            System.out.println(Thread.currentThread().getName()+"获取了锁" + myPath);
        }
        return lockRes;
    }

    @Override
    public Boolean unlock(String path, String flag) {
        //删除当前序号节点
        try {
            System.out.println(Thread.currentThread().getName()+"释放锁:" +  myPath);
            zkclient.delete(myPath, -1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
        return true;
    }

    @Override
    public Boolean exists(String path) {
        Stat exists = null;
        try {
            exists = zkclient.exists(path, false);
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (exists == null) {
            try {
                zkclient.create(rootPath, rootPath.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            } catch (KeeperException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return true;
    }


    public Boolean close() {
        try {
            zkclient.close();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return true;
    }


}

import com.lq.fitness.zk.yuansheng.linshi.ZkLock;

/**
 * 定义一个测试业务类
 */
public class YeWu implements Runnable {
    private ZkLock lock = new ZkLock();

    @Override
    public void run() {
        //给当前线程生成一个id
        try {
            //如果是if,没获取锁就直接退出,while表示直到获取锁为止
            if (!lock.lock(null, null)) {
                System.out.println("获取锁失败:"+Thread.currentThread().getName());
                return;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            lock.TEST_NO1++;
            lock.TEST_NO1++;
            lock.TEST_NO1++;
            lock.TEST_NO1++;
            lock.TEST_NO1++;
            lock.TEST_NO1++;
            lock.TEST_NO1++;
            lock.TEST_NO1++;
            lock.TEST_NO1++;
            lock.TEST_NO1++;
            System.out.println(ZkLock.TEST_NO1);
        } finally {
            //解锁
            lock.unlock(null, null);
            lock.close();
        }
    }
}
/**
 * 启动测试
 */
public class Test {
    public static void main(String[] args) {
        //启动100个线程去对 NonBlockZk.TEST_NO 做++
        for (int i = 0; i < 200; i++) {
            new Thread(new YeWu()).start();
        }
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

IT界的老菜鸟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值