ZooKeeper实现分布式锁的优点:ZK可以创建持久节点、临时节点和顺序节点。临时节点在会话结束之后,自动被删除。即使发生宕机,只要超出心跳时间,就会断开会话,从而删除临时节点,不会造成死锁的现象。
- 指定竞争的资源,在ZK下生成持久节点。
- 在持久节点下,生成若干临时顺序节点,尝试获取锁。
- 判断该节点是否是序号最小的节点,若是,则获取锁;若不是,则阻塞。
- 通过ZK的Watcher监听上一个节点的事件,满足要求则解除阻塞。重复3操作。
- 最后所有的节点都获得了锁,再断开ZK的会话连接,删除所有临时节点。
package com.mzs.lock;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.logging.Logger;
public class DistributeLock implements Watcher, Lock {
private static Logger logger = Logger.getLogger("com.mzs.lock.DistributeLock");
// 根节点路径
private final String path = "/root";
private ZooKeeper zooKeeper;
// 会话超时时间
private final static int SESSION_TIME_OUT = 50000;
// 当前锁
private String currentLock;
// 上一个锁
private String waitLock;
// 竞争的资源
private String lockName;
private CountDownLatch countDownLatch;
public DistributeLock(String url, String lockName) {
this.lockName = lockName;
try {
// 建立zookeeper会话
zooKeeper = new ZooKeeper(url, SESSION_TIME_OUT, this);
// 根节点是否存在
Stat stat = zooKeeper.exists(path, false);
if (stat == null) {
// 创建持久节点,并拥有所有权
zooKeeper.create(path, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
}
/**
* 加锁(普通获取或者重入获取)
*/
@Override
public void lock() {
// 成功获取
if (tryLock())
logger.info("[" + Thread.currentThread().getName() + "] --- get --- [" + lockName + "]");
else {
try {
// 等待获取
waitLock(waitLock, SESSION_TIME_OUT);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 等待锁
* @param waitTime 超时时间
* @return 是否成功等待
*/
private boolean waitLock(String waitLock, long waitTime) throws KeeperException, InterruptedException {
Stat stat = zooKeeper.exists(path + "/" + waitLock, true);
// 等待锁中有节点与之对应
if (stat != null) {
countDownLatch = new CountDownLatch(1);
try {
logger.info("[" + Thread.currentThread().getName() + "] --- wait --- [" + path + "/" + waitLock + "]");
// 等待时长为waitTime毫秒
boolean bool = countDownLatch.await(waitTime, TimeUnit.MILLISECONDS);
String curentThreadName = Thread.currentThread().getName();
logger.info("[" + curentThreadName + "] --- " + (!bool ? "wait for timeout,stop to get the lock" : "got the lock in the valid time"));
// 等待结束
countDownLatch = null;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return true;
}
/**
* 加锁(优先考虑中断)
*
* @throws InterruptedException 被中断异常
*/
@Override
public void lockInterruptibly() throws InterruptedException {
lock();
}
/**
* 尝试获得锁
* @return 是否获得锁
*/
@Override
public boolean tryLock() {
String separateStr = "/";
String separateStr2 = "-";
// 临时顺序节点的路径
String path_1 = path + separateStr + lockName + separateStr2;
try {
// 创建临时顺序节点
currentLock = zooKeeper.create(path_1, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
logger.info("[" + currentLock + "] --- " + " was created");
// 定义一个根节点下所有子节点组成的列表
List<String> nodeChildren = zooKeeper.getChildren(path, false);
// 定义一个存储包含lockName的节点的列表
List<String> nodeList = new ArrayList<>();
// 从nodeChildren中取出所有包含lockName的所有节点,加入到nodeList中
for (String nodeElem : nodeChildren) {
String node = nodeElem.split(separateStr2)[0];
if (node.equals(lockName)) {
nodeList.add(nodeElem);
}
}
// 排序nodeList(从小到大)
Collections.sort(nodeList);
logger.info("[" + Thread.currentThread().getName() + "] --- corresponding the lock [" + currentLock + "]");
// 若当前锁对应的是最小节点,则认为取得了锁
if (currentLock.equals(path + separateStr + nodeList.get(0))) {
return true;
}
// 通过当前锁查看它所对应的节点
String currentNode = currentLock.substring(currentLock.lastIndexOf(separateStr) + 1);
// 二分查找法查找上一个节点
int index = Collections.binarySearch(nodeList, currentNode) - 1;
// 将上一个节点标记一个等待锁
waitLock = nodeList.get(index);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 表示获取锁失败
return false;
}
/**
* 超时时间内,尝试获得锁
*
* @param time 超时时间
* @param unit 时间单位
* @return 是否成功获得锁
*/
@Override
public boolean tryLock(long time, TimeUnit unit) {
try {
// 已经成功获取锁
if (tryLock())
return true;
// 等待获取锁
return waitLock(waitLock, time);
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* 释放锁
*/
@Override
public void unlock() {
try {
// 删除当前锁对应的节点
zooKeeper.delete(currentLock, -1);
logger.info("[" + Thread.currentThread().getName() + "] --- release --- [" + currentLock + "]");
// 释放当前锁
currentLock = null;
zooKeeper.close();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
}
@Override
public Condition newCondition() {
return null;
}
/**
* 监听节点
* @param event 节点事件
*/
@Override
public void process(WatchedEvent event) {
if (countDownLatch != null) {
countDownLatch.countDown();
}
/*if (Event.KeeperState.SyncConnected == event.getState()) {
if (Event.EventType.None == event.getType() && event.getPath() == null) {
logger.info("establish the session");
} else if (Event.EventType.NodeCreated == event.getType()) {
logger.info("node [" + event.getPath() + "] was created");
} else if (Event.EventType.NodeDeleted == event.getType()) {
logger.info("node [" + event.getPath() + "] was deleted");
} else if (Event.EventType.NodeDataChanged == event.getType()) {
logger.info("node [" + event.getPath() + "] data changed");
} else if (Event.EventType.NodeChildrenChanged == event.getType()) {
logger.info("node [" + event.getPath() + "] children node changed");
}
}*/
}
}
package com.mzs.lock;
import java.util.logging.Logger;
public class TestDistributeLock {
private static Logger logger = Logger.getLogger("com.mzs.lock.TestDistributeLock");
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
DistributeLock distributeLock = null;
try {
distributeLock = new DistributeLock("192.168.101.199:2181", "lock");
distributeLock.lock();
logger.info("[" + Thread.currentThread().getName() + "] --- running" );
} finally {
if (distributeLock != null)
distributeLock.unlock();
}
}
});
thread.start();
}
}
}
代码选自 --- https://www.cnblogs.com/liuyang0/p/6800538.html