分布式锁设计
业务需求:为售票机设计分布式锁 ,多台机器同时售票,保证每台机器当前售票业务结束后,才可以开始下一次售票 。
思路:利用zookeeper临时有序节点的特性为每次售票生成唯一的识别(也就是00000000001,000000000002这种格式的字符串),对每个业务的识别进行排序后,设定排在第一个的识别为获取到锁的识别,只有获取到锁的售票业务才可以进行售票,否则就只能等待,直到排在前面的识别释放了锁(也就是前一个节点被删除),才可以获取到锁。
创建锁的工具类
public class MyLock {
//计数器对象
CountDownLatch countDownLatch = new CountDownLatch(1);
String IP = "192.168.164.134:2181";
ZooKeeper zooKeeper;
private static final String LOCK_ROOT_PATH = "/locks";
private static final String LOCK_NODE_PATH = "lock1_";
private String lockPath = "";
public MyLock() {
try {
zooKeeper = new ZooKeeper(IP, 500000, new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getType() == Event.EventType.None) {
if (event.getState() == Event.KeeperState.SyncConnected) {
System.out.println("连接创建成功");
countDownLatch.countDown();
}
}
}
});
//阻塞线程直到连接成功
countDownLatch.await();
} catch (Exception e) {
e.printStackTrace();
}
}
//获取锁
public void acquireLock() throws KeeperException, InterruptedException {
//创建锁
createLoke();
//尝试获取锁
attemptLock();
}
private void createLoke() throws KeeperException, InterruptedException {
Stat stat = zooKeeper.exists(LOCK_ROOT_PATH, false);
//判断根节点是否存在,不存在就创建
if (stat == null) {
zooKeeper.create(LOCK_ROOT_PATH, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
lockPath = zooKeeper.create(LOCK_ROOT_PATH + "/" + LOCK_NODE_PATH, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
System.out.println("节点创建成功" + lockPath);
}
private void attemptLock() throws KeeperException, InterruptedException {
List<String> children = zooKeeper.getChildren(LOCK_ROOT_PATH, false);
Collections.sort(children);
//lockPath:/locks/lock_0000000000000001格式
int indexOf = children.indexOf(lockPath.substring(LOCK_ROOT_PATH.length() + 1));
if (indexOf == 0) {
System.out.println("成功获取到锁");
return;
} else {
String prePath = children.get(indexOf - 1);
Stat stat = zooKeeper.exists(LOCK_ROOT_PATH +"/"+prePath, watcher);
if (stat == null) {
attemptLock();
} else {
synchronized (watcher) {
watcher.wait();
}
attemptLock();
}
}
}
Watcher watcher = new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getType() == Event.EventType.NodeDeleted) {
synchronized (this) {
watcher.notifyAll();
}
}
}
};
//释放锁
public void releaseLock() throws KeeperException, InterruptedException {
//删除临时有序节点
zooKeeper.delete(this.lockPath,-1);
zooKeeper.close();
System.out.println("锁已经释放"+this.lockPath);
}
}
测试
public class TicketSeller {
private void sell(){
System.out.println("售票开始。。。。。。。。。。。");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("售票结束#######################################################");
}
private void sellTicketWithLoke() throws KeeperException, InterruptedException {
MyLock myLock = new MyLock();
//获取锁
myLock.acquireLock();
sell();
//释放锁
myLock.releaseLock();
}
public static void main(String[] args) throws KeeperException, InterruptedException {
TicketSeller ticketSeller= new TicketSeller();
while (true){
ticketSeller.sellTicketWithLoke();
}
}
}