基于zookeeper实现分布式锁

前提

zooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是集群的管理者。提供了文件系统和通知机制。

在开发项目的过程中,很多大型项目都是分布式部署的,那么我们现在使用zookeeper实现一个分布式锁。

锁的动作

1、获取锁

2、释放锁

3、等待锁

4、死锁

基于zookeeper实现分布式锁思路

1、基于zookeeper提供的文件系统,可以将指定目录作为竞争的资源。同时使用有序目录结构,将后续竞争资源按照顺序排列标号。

2、使用通知机制监听子节点的变化,当线程1释放锁,删除目录时,其他竞争线程监听消息。

4.按照思路编写测试用例

public static void main(String [] args) throws InterruptedException {
    final ZkClient zkClient = new ZkClient(servers, 10000);
    boolean panss = zkClient.exists("/sys/app");
    if(panss){
        zkClient.deleteRecursive("/sys/app");
    }
    zkClient.createPersistent("/sys/app");
    final String ephemeralSequential1 = zkClient.createEphemeralSequential("/sys/app/lock", new Byte[0]);
    final String ephemeralSequential2 = zkClient.createEphemeralSequential("/sys/app/lock", new Byte[0]);
    List<String> subNodes = zkClient.getChildren("/sys/app");
    System.out.println("当前子节点情况:" + JSON.toJSONString(subNodes));
    final Object obj = Thread.currentThread();
    // 监听子节点变化
    final Long currentThreadId = Thread.currentThread().getId();
    System.out.println("当前主线程id:"+Thread.currentThread().getId());
    zkClient.subscribeChildChanges("/sys/app", new IZkChildListener() {
        public void handleChildChange(String s, List<String> list) throws Exception {
            System.out.println("节点发生变化:" + s + "当前线程id:"+Thread.currentThread().getId() + " list:" + JSON.toJSONString(list));
            if(list != null && list.size() !=0 && ("/sys/app/" + list.get(0)).equals(ephemeralSequential2)){
                ThreadMXBean tmx = ManagementFactory.getThreadMXBean();
                ThreadInfo info = tmx.getThreadInfo(currentThreadId);
                System.out.println("主线程状态:"+info.getThreadState());
                synchronized (obj){
                    obj.notify();
                }
            }
        }
    });
    new Thread(new Runnable() {
        public void run() {
            try {
                System.out.println("等待三秒后释放锁");
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            zkClient.delete(ephemeralSequential1);
        }
    }).start();
    System.out.println("主线程等待获取到锁");
    synchronized (obj){
        obj.wait();
    }
    System.out.println("主线程被唤醒,获取到锁了");
}

控制台打印情况

当前子节点情况:["lock0000000000","lock0000000001"]
当前主线程id:1
主线程等待获取到锁
等待三秒后释放锁
节点发生变化:/sys/app当前线程id:16 list:["lock0000000001"]
主线程状态:WAITING
主线程被唤醒,获取到锁了

测试完成。

编写工具类

import com.alibaba.fastjson.JSON;
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.jboss.netty.util.internal.StringUtil;

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.text.DecimalFormat;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * 分布式锁
 *
 * @author blackman
 * @date 2018/04/21 22:41
 *
 */
public class DistributedLock {

    private static String servers = "192.168.3.177:2181";

    private static String LOCK_PARENT_PATH = "/sys/app_lock";

    private static ZkClient zkClient;

    static{
        zkClient = new ZkClient(servers, 10000);
        boolean parentExist = existLock(LOCK_PARENT_PATH);
        if(!parentExist){
            zkClient.createPersistent(LOCK_PARENT_PATH, true);
        }
    }

    /**
     * 判断文件目录是否存在
     * @param path
     * @return
     */
    public static boolean existLock(String path){
        return zkClient.exists(path);
    }

    /**
     * 获取锁
     * @param lockPath
     */
    public static void getLock(String lockPath){
        // 创建有序临时目录
        final String ephemeralSequential1 = zkClient.createEphemeralSequential(LOCK_PARENT_PATH + "/" + lockPath, new Byte[0]);
        List<String> subNodes = zkClient.getChildren(LOCK_PARENT_PATH);
        if(ephemeralSequential1.equals(subNodes.get(0))){
            // 当前为第一位有序目录-获取到锁
            return;
        }
        final Object obj = Thread.currentThread();
        zkClient.subscribeChildChanges(LOCK_PARENT_PATH, new IZkChildListener() {
            public void handleChildChange(String s, List<String> list) throws Exception {
                if(list != null
                        && list.size() !=0
                        && (LOCK_PARENT_PATH + "/" + list.get(0)).equals(ephemeralSequential1)){
                    synchronized (obj){
                        obj.notify();
                    }
                }
            }
        });
        synchronized (obj){
            try {
                obj.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 还锁
     * @param lockPath
     */
    public static void alsoLock(String lockPath){
        zkClient.delete(LOCK_PARENT_PATH + "/" +lockPath);
    }
}

不太会写文章,请见谅

【我是码农,也不是码农】软件 – 注重思想与逻辑

我的博客文章地址

源码下载地址:https://download.csdn.net/download/javalearnnet/10713112

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小黑鸣

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

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

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

打赏作者

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

抵扣说明:

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

余额充值