1、为什么需要分布式锁
因为在集群环境中,每个类中的全局变量在每个jvm中都保存独立的一份,互不影响,不能做到保证全局唯一。所以需要分布式锁;而zookeeper中临时节点的名称唯一性以及会话保持一致性还有节点事件通知的特性决定了zookeeper可以很好的实现分布式锁;
2、代码实现
public abstract class ZookeeperAbstractLock implements ExtLock {
// 集群连接地址
protected String CONNECTION = "127.0.0.1:2181";
// zk客户端连接
protected ZkClient zkClient = new ZkClient(CONNECTION);
// path路径
protected String lockPath = "/path";
protected CountDownLatch countDownLatch = new CountDownLatch(1);
public void getLock() {
if (tryLock()) {
System.out.println("####获取锁成功######");
} else {
waitLock();
getLock();
}
}
// 获取锁
abstract boolean tryLock();
// 等待锁
abstract void waitLock();
public void unLock() {
if (zkClient != null) {
System.out.println("#######释放锁#########");
zkClient.close();
}
}
public class ZookeeperDistrbuteLock extends ZookeeperAbstractLock {
@Override
boolean tryLock() {
try {
zkClient.createEphemeral(lockPath);
return true;
} catch (Exception e) {
return false;
}
}
@Override
void waitLock() {
// 使用zk临时事件监听
IZkDataListener iZkDataListener = new IZkDataListener() {
public void handleDataDeleted(String path) throws Exception {
if (countDownLatch != null) {
countDownLatch.countDown();
}
}
public void handleDataChange(String arg0, Object arg1) throws Exception {
}
};
// 注册事件通知
zkClient.subscribeDataChanges(lockPath, iZkDataListener);
if (zkClient.exists(lockPath)) {
countDownLatch = new CountDownLatch(1);
try {
countDownLatch.await();
} catch (Exception e) {
// TODO: handle exception
}
}
// 监听完毕后,移除事件通知
zkClient.unsubscribeDataChanges(lockPath, iZkDataListener);
}
}
public class OrderService implements Runnable {
private OrderNumGenerator orderNumGenerator = new OrderNumGenerator();
private ExtLock extLock = new ZookeeperDistrbuteLock();
public void run() {
getNumber();
}
public void getNumber() {
try {
extLock.getLock();
String number = orderNumGenerator.getNumber();
System.out.println("线程:" + Thread.currentThread().getName() + ",生成订单id:" + number);
} catch (Exception e) {
} finally {
extLock.unLock();
}
}
public static void main(String[] args) {
System.out.println("多线程生成number");
// OrderService orderService = new OrderService();
for (int i = 0; i < 100; i++) {
new Thread(new OrderService()).start();
}
}
}