Zookeeper锁的简单实现

最近正在学习ZooKeeper,这个工具有两个比较典型的入门小应用就是配置文件管理和锁实现。

自己写了锁实现分布式程序的不同线程同步处理。

想想有很多漏洞,如断网,服务器不可用等情况,不能完全实现锁安全和准确,只能做到相对安全。


需要在zookeeper上先建立一个/lock目录,存放锁的相关信息。

锁的工具类:


package cn.free8day.zk.lock;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;

/**
 * 锁工具
 *
 * @author liubq
 */
public class ZkLock {

    /**
     * 构造
     *
     * @param zk
     */
    public ZkLock(ZooKeeper zk) {
        this.zk = zk;
    }

    private ZooKeeper zk;

    //锁目录
    private String LOCK_PATH = "/lock";

    private String waitPath = null;

    private String thisPath = null;

    final Lock lock = new ReentrantLock();

    final Condition zkWait = lock.newCondition();

    /**
     * 加锁
     * @throws Exception
     */
    public void lock() throws Exception {
        thisPath = zk.create(LOCK_PATH + "/sub", Thread.currentThread().getName().getBytes(),
                Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        lockByzk(false);
    }

    /**
     * 解锁
     *
     * @throws Exception
     */
    public void unLock() throws Exception {
        unLockByzk();
    }

    /**
     * 解锁
     *
     * @throws Exception
     */
    private void unLockByzk() throws Exception {
        zk.delete(this.thisPath, -1);
    }

    /**
     * 加锁
     *
     * @param path
     * @throws Exception
     */
    private void lockByzk(boolean isEventMode) throws Exception {
        List<String> children = zk.getChildren(LOCK_PATH, null);
        Collections.sort(children);
        String thisNode = thisPath.substring(LOCK_PATH.length() + 1);
        int index = children.indexOf(thisNode);
        // 确实是最小节点  
        if (index == 0) {
            if (isEventMode) {
                lock.lock();
                try {
                    zkWait.signalAll();
                }
                finally {
                    lock.unlock();
                }
            }
            return;
        }
        // 获得排名比thisPath前1位的节点  
        this.waitPath = LOCK_PATH + "/" + children.get(index - 1);
        // 在waitPath上注册监听器, 当waitPath被删除时, zookeeper会回调监听器的process方法  

        if (zk.exists(waitPath, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                try {
                    // 发生了waitPath的删除事件  
                    if (event.getType() != EventType.NodeDeleted) {
                        return;
                    }
                    if (!event.getPath().equals(waitPath)) {
                        return;
                    }
                    lockByzk(true);
                }
                catch (Exception e) {
                    zkLogger.error(e);
                }

            }

        }) == null) {
            //没有成功加入监听,则循环重新处理
            lockByzk(isEventMode);
        }
        else {
            if (!isEventMode) {
                lock.lock();
                try {
                    children = zk.getChildren(LOCK_PATH, null);
                    Collections.sort(children);
                    thisNode = thisPath.substring(LOCK_PATH.length() + 1);
                    index = children.indexOf(thisNode);
                    // 确实是最小节点  
                    if (index == 0) {
                        return;
                    }
                    else {
                        zkWait.await();
                    }
                }
                finally {
                    lock.unlock();
                }
            }
        }
    }
}

测试工具类,只是检查测试。


package cn.free8day.zk.lock;

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

import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooKeeper;

public class TMainTest {

    private static final int len = 190;

    private final static CountDownLatch latchMain = new CountDownLatch(len);

    private static List<Integer> count = new ArrayList<Integer>();

    public static void main(String[] args) throws Exception {
        TMainTest test = new TMainTest();
        test.connectZookeeper();
        int allTi = 1500;
        test.zk.setData("/ti", String.valueOf(allTi).getBytes(), -1);
        for (int i = 0; i < len; i++) {
            Thread t = new Thread("THREAD" + i) {
                public void run() {
                    try {
                        TMainTest dl = new TMainTest();
                        for (int i = 0; i < 10; i++) {
                            Thread.sleep(10);
                            dl.consume();
                        }
                        latchMain.countDown();
                    }
                    catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };
            t.start();
        }

        latchMain.await();

        for (int i = 0; i < allTi; i++) {
            if (count.get(allTi - 1 - i) != i) {
                System.out.println("并发错误!");
                return;
            }
        }
        System.out.println("并发成功!");
    }

    private void consume() throws Exception {
        ZkLock lockObj = new ZkLock(connectZookeeper());
        lockObj.lock();
        byte[] tis = zk.getData("/ti", null, null);
        String ti = new String(tis);
        int ticketCount = Integer.valueOf(ti).intValue();
        if (ticketCount > 0) {
            ticketCount = ticketCount - 1;
            zk.setData("/ti", String.valueOf(ticketCount).getBytes(), -1);
            System.out.println(Thread.currentThread().getName() + " 消耗了一张票" + " 现在:" + ticketCount);
            // System.out.println(ticketCount);
            count.add(ticketCount);

        }

        lockObj.unLock();
    }

    private CountDownLatch latch = new CountDownLatch(1);

    private ZooKeeper zk = null;

    public ZooKeeper connectZookeeper() throws Exception {
        if (zk != null) {
            return zk;
        }
        zk = new ZooKeeper("192.168.91.198:2181", 5000, new Watcher() {
            public void process(WatchedEvent event) {
                try {
                    // 连接建立时, 打开latch, 唤醒wait在该latch上的线程  
                    if (event.getState() == KeeperState.SyncConnected) {
                        latch.countDown();
                        return;
                    }
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        //  等待连接建立  
        latch.await();
        return zk;
    }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值