线程获取锁时,在/locks
节点下创建临时有序号节点,需要注意的是,有序号的节点序号是递增的
create -e -s /locks/seq-
同时获取/locks
节点的子节点列表,并对子节点列表进行排序,并获取当前节点的索引
如果当前节点是索引最小的节点,直接返回,表示获取到锁
如果当前节点不是索引最小的节点,设置监听,监听前一个索引节点的值的变化,同时线程等待
实际上,以上两步已经将获取锁的顺序设置好了
获取到锁的线程执行完之后,删除节点,表示释放锁。此时将触发下一个节点的监听回调,唤醒等待的线程
重复步骤5,直到所有的锁都释放
分布式锁示例代码DistributedLock.java
package xin.yangshuai.zookeeper01.case2;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
public class DistributedLock {
// 注意:逗号左右不能有空格
private String connectString = "192.168.145.132:2181,192.168.145.133:2181,192.168.145.134:2181";
// 2000毫秒
private int sessionTimeout = 2000;
private ZooKeeper zkClient;
private CountDownLatch countDownLatch = new CountDownLatch(1);
private CountDownLatch waitLatch = new CountDownLatch(1);
private String currentMode;
private String listeningNode;
public DistributedLock() throws IOException, InterruptedException, KeeperException {
// 获取连接
zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
// 初始化时,会执行一次
System.out.println(watchedEvent.getPath() + "----------------------------");
// 启动时会触发,表示已经连接上zookeeper了,放行
if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
countDownLatch.countDown();
}
// 触发监听,监听的节点被删除了,放行
if (watchedEvent.getType() == Event.EventType.NodeDeleted && watchedEvent.getPath().equals(listeningNode)) {
waitLatch.countDown();
}
}
});
countDownLatch.await();
// 已经连接上zookeeper后,继续执行
// 判断父节点/locks是否存在
Stat stat = zkClient.exists("/locks", false);
if (stat == null) {
// 创建父节点/locks
zkClient.create("/locks", "locks".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
}
// 获取锁
public void lock() throws KeeperException, InterruptedException {
// 创建临时有序号节点
currentMode = zkClient.create("/locks/seq-", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
Thread.sleep(10);
// 获取/locks节点的子节点列表
List<String> children = zkClient.getChildren("/locks", false);
if (children.size() == 1) {
// 只有一个节点,直接获取锁,返回
return;
}
Collections.sort(children);
// 获取节点名称,seq-00000000
String thisNode = currentMode.substring("/locks/".length());
// 获取节点在集合中的位置
int index = children.indexOf(thisNode);
if (index == 0) {
// 是第一个节点,直接获取锁,返回
return;
}
// 获取前一个索引的节点
listeningNode = "/locks/" + children.get(index - 1);
// 监听前一个节点的值的变化
zkClient.getData(listeningNode, true, null);
// 线程等待,直到上一个节点释放锁(被删除)时,将触发监听回调,会设置此处放行
System.out.println(Thread.currentThread().getName() + " 未获取到锁,等待");
waitLatch.await();
// 上一个节点释放锁,继续执行,表示获取到锁
}
// 解锁
public void unlock() throws KeeperException, InterruptedException {
// 删除节点,表示释放锁
zkClient.delete(currentMode, -1);
}
}
测试方法示例代码DistributedLockTest.java
package xin.yangshuai.zookeeper01.case2;
import org.apache.zookeeper.KeeperException;
import java.io.IOException;
public class DistributedLockTest {
public static void main(String[] args) throws InterruptedException, IOException, KeeperException {
DistributedLock lock1 = new DistributedLock();
DistributedLock lock2 = new DistributedLock();
new Thread(new Runnable() {
@Override
public void run() {
try {
lock1.lock();
System.out.println(Thread.currentThread().getName() + " 获取到锁");
Thread.sleep(5000);
lock1.unlock();
System.out.println(Thread.currentThread().getName() + " 释放锁");
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
}
}, "线程1").start();
new Thread(new Runnable() {
@Override
public void run() {
try {
lock2.lock();
System.out.println(Thread.currentThread().getName() + " 获取到锁");
Thread.sleep(5000);
lock2.unlock();
System.out.println(Thread.currentThread().getName() + " 释放锁");
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
}
}, "线程2").start();
}
}
仅供参考
官网地址:https://curator.apache.org/docs/about
测试方法示例代码CuratorLockTest.java
package xin.yangshuai.zookeeper01.case3;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;
public class CuratorLockTest {
// 注意:逗号左右不能有空格
private static String connectString = "192.168.145.132:2181,192.168.145.133:2181,192.168.145.134:2181";
// 2000毫秒
private static int sessionTimeout = 2000;
public static void main(String[] args) {
// 创建分布式锁1
InterProcessMutex lock1 = new InterProcessMutex(getCuratorFramework(), "/locks");
// 创建分布式锁2
InterProcessMutex lock2 = new InterProcessMutex(getCuratorFramework(), "/locks");
new Thread(new Runnable() {
@Override
public void run() {
try {
lock1.acquire();
System.out.println("线程1 获取到锁");
lock1.acquire();
System.out.println("线程1 再次获取到锁");
Thread.sleep(5000);
lock1.release();
System.out.println("线程1 释放锁");
lock1.release();
System.out.println("线程1 再次释放锁");
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
lock2.acquire();
System.out.println("线程2 获取到锁");
lock2.acquire();
System.out.println("线程2 再次获取到锁");
Thread.sleep(5000);
lock2.release();
System.out.println("线程2 释放锁");
lock2.release();
System.out.println("线程2 再次释放锁");
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
// 连接zookeeper
private static CuratorFramework getCuratorFramework() {
ExponentialBackoffRetry policy = new ExponentialBackoffRetry(3000, 3);
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString(connectString)
.connectionTimeoutMs(sessionTimeout)
.sessionTimeoutMs(sessionTimeout)
.retryPolicy(policy).build();
// 启动客户端
client.start();
System.out.println("zookeeper客户端启动成功");
return client;
}
}
仅供参考