一,阻塞式和非阻塞式
“阻塞”和“非阻塞”只是一个人为的概念,“阻塞”说的是一个线程去尝试获取锁,如果失败,就阻塞在这里,监听到持有锁的线程释放,再次去尝试获取。 而“非阻塞”是我尝试一次失败了,失败就拉倒,直接不要了。
而这两个区别是通过代码来体现的。
二,zk非阻塞式锁的实现
(1)首先定义“锁”的接口,包括上锁,解锁,锁是否存在。
public interface ZkLocker {/*** 能否上锁成功*/boolean lock();/*** 解锁 能否解锁成功*/boolean unlock();/*** 判断锁是否存在*/boolean exists();
}
(2)定义基类,包含对于zookeeper结点的一些基本操作,包括和zk连接,创建结点,删除结点...等等。所有的锁(不管什么锁都要继承这个类)
public class ZkLockerBase {//客户端和服务端连接protected ZooKeeper zooKeeper;//锁的根节点的名称protected String rootNodeName = "/zk-lock";//锁的名称protected String lockName;/*** 默认连接到指定的服务端* @throws IOException* @throws InterruptedException*/public ZkLockerBase() {this("localhost:2181");//服务器ip地址,改。}public ZkLockerBase(String connectString){try {CountDownLatch latch = new CountDownLatch(1);zooKeeper = new ZooKeeper(connectString, 5000000, new Watcher() {@Overridepublic void process(WatchedEvent event) {switch (event.getType()) {case None:if (event.getState().equals(Event.KeeperState.SyncConnected)) {latch.countDown();}break;case NodeCreated:break;case NodeDeleted:break;case NodeChildrenChanged:break;}}});latch.await();createRootNode();//连接之后创建根节点}catch (Exception e){e.printStackTrace();}}/*** 创建根节点,用于存放所有的节点或者子节点实现的锁*/public void createRootNode(){//判断我的root是否存在if (!exists(rootNodeName)){createNode(rootNodeName,CreateMode.CONTAINER);}}public boolean exists(String nodeName){try {Stat exists = zooKeeper.exists(nodeName, false);return exists != null;}catch (Exception e){}return false;}/*** return真正的名字* @param nodeName 创建结点名字* @param createMode 创建结点的类型* @return 创建成功之后结点的名字*/public String createNode(String nodeName, CreateMode createMode) {try {return zooKeeper.create(nodeName, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, createMode);}catch (Exception e){e.printStackTrace();return null;//创建失败}}/*** 删除结点* @param nodeName* @return*/public boolean deleteNode(String nodeName){try {zooKeeper.delete(nodeName,-1);//跳过版本检查return true;}catch (Exception e){e.printStackTrace();}return false;}
}
(3)zk非阻塞式锁实现
public class ZkNodeNoneBlockingLock extends ZkLockerBase implements ZkLocker {/*** 默认连接* @param lockName*/public ZkNodeNoneBlockingLock(String lockName){super();this.lockName = lockName;}/*** 可指定连接* @param connectString* @param lockName*/public ZkNodeNoneBlockingLock(String connectString,String lockName){super(connectString);this.lockName = lockName;}@Overridepublic boolean lock() {return createNode(rootNodeName + "/" + lockName,CreateMode.EPHEMERAL) != null;}@Overridepublic boolean unlock() {return deleteNode(rootNodeName + "/" + lockName);}@Overridepublic boolean exists() {return exists(rootNodeName + "/" + lockName);}
}
接下来跑测试代码来测试,分别测试在单线程和多线程并发状态下的结果。
测试代码: