之前已经实现过基于redis的分布式锁
这次用zookeeper来实现.
原理:ZooKeeper有四种形式的目录节点,四种CreateMode
- PERSISTENT:持久化目录节点,存储的数据不会丢失。
- PERSISTENT_SEQUENTIAL:顺序自动编号的持久化目录节点,存储的数据不会丢失,并且根据当前已近存在的节点数自动加 1,然后返回给客户端已经成功创建的目录节点名。
- EPHEMERAL:临时目录节点,一旦创建这个节点的客户端与服务器端口也就是session 超时,这种节点会被自动删除。
- EPHEMERAL_SEQUENTIAL:临时自动编号节点,一旦创建这个节点的客户端与服务器端口也就是session 超时,这种节点会被自动删除,并且根据当前已近存在的节点数自动加 1,然后返回给客户端已经成功创建的目录节点名。
用EPHEMERAL_SEQUENTIAL即可实现分布式锁.当获取锁的时候,节点会自动增加,如果你的节点是最小的,那么你就获取锁.不然就等待.(只需等待比你大的节点是否还存在,如果不存在,重复获取)
##代码
package demo.zookeeper.lock;
import demo.zookeeper.AbstractZooKeeper;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import java.io.IOException;
import java.util.Comparator;
import java.util.List;
/**
* 基于Zookeeper的分布式锁
* Created by yuyufeng on 2017/8/22.
*/
public class LockUtil {
//锁失效时间10秒
private static final int TIME_OUT = 10000;
private static final String HOST = "localhost:2181";
private static ZooKeeper zookeeper;
private static String ROOT_PATH = "/lockdata";
static {
try {
zookeeper = new ZooKeeper(HOST, TIME_OUT, new AbstractZooKeeper());
try {
//建立锁节点存放根目录
if (zookeeper.exists(ROOT_PATH, false) == null) {
zookeeper.create(ROOT_PATH, "true".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
} catch (Exception e) {}
} catch (IOException e) {}
}
/**
* 获取一个锁
* @param key 同步监听对象
* @return
*/
public static boolean getLock(String key) {
Long beginTime = System.currentTimeMillis();
try {
if (zookeeper.exists(ROOT_PATH + "/" + key, false) == null) {
zookeeper.create(ROOT_PATH + "/" + key, "true".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
} catch (KeeperException e) {} catch (InterruptedException e) {}
String result = null;
try {
result = zookeeper.create(ROOT_PATH + "/" + key + "/", "true".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
} catch (KeeperException e) {} catch (InterruptedException e) {}
result = result.substring(result.lastIndexOf("/") + 1, result.length());
return lockHandler(key, beginTime, result);
}
private static boolean lockHandler(String key, Long beginTime, String result){
List<String> list = null;
try {
list = zookeeper.getChildren(ROOT_PATH + "/" + key, false);
} catch (KeeperException e) {} catch (InterruptedException e) {}
if (list == null || list.size() == 0) {
System.out.println(Thread.currentThread().getName()+"获取锁异常,失败!");
return false;
}
System.out.println("==========================================");
list.sort(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return Integer.valueOf(o1) - Integer.valueOf(o2);
}
});
System.out.println(Thread.currentThread().getName()+"获取锁结果: " + result + " :" + list.get(0));
if (result.equals(list.get(0))) {
System.out.println(Thread.currentThread().getName()+ROOT_PATH + "/" + key + " 获取锁成功");
return true;
}
//为获取锁失败,监听当前节点之前的节点,如之前的节点已经不存在,再次获取.
String beforeNode = list.get(0);
for (int i = 0; i < list.size(); i++) {
if (result.equals(list.get(i))) {
beforeNode = list.get(--i);
break;
}
}
//监听上一个节点5秒,如果上一个节点不存在,则再去获取锁
while ((System.currentTimeMillis() - beginTime) < 5000) {
System.out.println(Thread.currentThread().getName()+" 正在监听节点 " + ROOT_PATH + "/" + key + "/" + beforeNode);
try {
if (zookeeper.exists(ROOT_PATH + "/" + key + "/" + beforeNode, false) == null) {
return lockHandler(key, beginTime, result);
}
} catch (KeeperException e) {} catch (InterruptedException e) {}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"之前的节点" + beforeNode + "未释放,放弃获取锁");
try {
zookeeper.delete(ROOT_PATH + "/" + key + "/" + result, -1);
} catch (InterruptedException e) {} catch (KeeperException e) {}
return false;
}
/**
* 删除一个监听对象下所有的节点
* @param key
* @return
*/
public static boolean unLockNode(String key){
List<String> list = null;
try {
list = zookeeper.getChildren(ROOT_PATH + "/" + key, false);
} catch (KeeperException e) { } catch (InterruptedException e) { }
if (list != null) {
for (String s : list) {
try {
zookeeper.delete(ROOT_PATH + "/" + key + "/" + s, -1);
} catch (InterruptedException e) { } catch (KeeperException e) { }
}
}
return true;
}
/**
* 解锁当前获取锁的节点
*
* @param key
* @return
* @throws InterruptedException
* @throws KeeperException
*/
public static boolean unLock(String key) {
System.out.println("开始解锁-------------");
List<String> list = null;
try {
list = zookeeper.getChildren(ROOT_PATH + "/" + key, false);
} catch (KeeperException e) {} catch (InterruptedException e) {}
if (list == null || list.size() == 0) {
System.out.println(Thread.currentThread().getName()+"锁不存在,或已经被解锁,成功!");
return false;
}
System.out.println("==========================================");
list.sort(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return Integer.valueOf(o1) - Integer.valueOf(o2);
}
});
try {
zookeeper.delete(ROOT_PATH + "/" + key + "/" + list.get(0), -1);
} catch (InterruptedException e) {} catch (KeeperException e) {}
try {
if (zookeeper.exists(ROOT_PATH + "/" + key + "/" + list.get(0), false) != null) {
System.out.println(Thread.currentThread().getName()+" 解锁失败!");
return false;
}
} catch (KeeperException e) {} catch (InterruptedException e) {}
System.out.println(Thread.currentThread().getName()+" 解锁成功!");
return true;
}
public static void main(String[] args) {
// System.out.println(LockUtil.getLock("a1"));
// System.out.println(LockUtil.unLock("a1"));
new Thread() {
@Override
public void run() {
System.out.println(LockUtil.getLock("a1"));
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(LockUtil.unLock("a1"));
}}.start();
new Thread() {
@Override
public void run() {
System.out.println(LockUtil.getLock("a1"));
System.out.println(LockUtil.unLock("a1"));
}}.start();
new Thread() {
@Override
public void run() {
System.out.println(LockUtil.getLock("a1"));
}}.start();
LockUtil.unLockNode("a1");
}
}
##运行
==========================================
==========================================
==========================================
Thread-2获取锁结果: 0000000301 :0000000300
Thread-0获取锁结果: 0000000300 :0000000300
Thread-1获取锁结果: 0000000302 :0000000300
Thread-0/lockdata/a1 获取锁成功
Thread-2 正在监听节点 /lockdata/a1/0000000300
true
Thread-1 正在监听节点 /lockdata/a1/0000000301
Thread-2 正在监听节点 /lockdata/a1/0000000300
Thread-1 正在监听节点 /lockdata/a1/0000000301
Thread-2 正在监听节点 /lockdata/a1/0000000300
Thread-1 正在监听节点 /lockdata/a1/0000000301
Thread-2 正在监听节点 /lockdata/a1/0000000300
Thread-1 正在监听节点 /lockdata/a1/0000000301
开始解锁-------------
==========================================
Thread-0 解锁成功!
true
Thread-2 正在监听节点 /lockdata/a1/0000000300
==========================================
Thread-2获取锁结果: 0000000301 :0000000301
Thread-2/lockdata/a1 获取锁成功
true
Thread-1 正在监听节点 /lockdata/a1/0000000301
Thread-1 正在监听节点 /lockdata/a1/0000000301
Thread-1 正在监听节点 /lockdata/a1/0000000301
Thread-1 正在监听节点 /lockdata/a1/0000000301
Thread-1 正在监听节点 /lockdata/a1/0000000301
Thread-1 正在监听节点 /lockdata/a1/0000000301
Thread-1之前的节点0000000301未释放,放弃获取锁
false
使用zookeeper相对使用redis作为分布式锁的区别
优势 | 缺点 |
---|---|
zook可作读写锁.(实现方式:使用读写两种方式,获取读锁时当获取的节点前面有比自己小的写锁节点存在,有则获取失败.获取写锁则必须当前节点为最小的节点) | 在实现过程中可以看出,效率没有redis的高,固应对高并发能量没有使用redis做分布式锁强 |