通过用zk创建临时节点的特性,实现分布式锁;
当前只有一个线程创建成功,然后close之后,该节点被删除;
其他线程可以再次争抢。
实现一个生成订单号,几个线程生成几个订单号,模仿多jvm对应多个线程
public class OrderNumCreateUtil {
private static int number = 0;
public String getOrderNumber(){
return " 生成订单号:" + (++ number);
}
}
public interface ZkLock {
public void zklock();
public void zkUnlock();
}
利用模板方法模式,把需要经常改的东西抽象出来,交给子类去实现
public abstract class ZkAbstractLock implements ZkLock{
public static final String ZKSERVER = "47.100.41.55:2181";
public static final int TIME_OUT = 45 * 1000;
ZkClient zkClient = new ZkClient(ZKSERVER, TIME_OUT);
protected String path = "/zklock0401";
protected CountDownLatch countDownLatch = null;
@Override
public void zklock() {
if(tryZkLock()){
System.out.println(Thread.currentThread().getName() + " 占用锁成功。");
}else{
waitZkLock();
zklock();
}
}
public abstract void waitZkLock();
public abstract boolean tryZkLock();
@Override
public void zkUnlock() {
if(zkClient != null){
zkClient.close();
}
System.out.println(Thread.currentThread().getName() + " 释放锁成功。");
System.out.println();
System.out.println();
}
}
public class ZkDistributedLock extends ZkAbstractLock{
@Override
public void waitZkLock() {
//监听path,1,数据是否改变或删除
IZkDataListener iZkDataListener = new IZkDataListener() {
@Override
public void handleDataDeleted(String arg0) throws Exception {
if(countDownLatch != null){
countDownLatch.countDown();
}
}
@Override
public void handleDataChange(String arg0, Object arg1) throws Exception {
}
};
zkClient.subscribeDataChanges(path, iZkDataListener);
if(zkClient.exists(path)){
//只能等着,因为锁已经被其他线程占领了
countDownLatch = new CountDownLatch(1);
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
zkClient.unsubscribeDataChanges(path, iZkDataListener);
}
@Override
public boolean tryZkLock() {
try {
//创建临时节点
zkClient.createEphemeral(path);
return true;
} catch (Exception e) {
return false;
}
}
}
public class OrderService {
private OrderNumCreateUtil orderNumCreateUtil = new OrderNumCreateUtil();
private ZkLock zklock = new ZkDistributedLock();
public void getOrderNum(){
zklock.zklock();
try {
System.out.println("获得编号:----->:" + orderNumCreateUtil.getOrderNumber());
} catch (Exception e) {
}finally{
zklock.zkUnlock();
}
}
}
public class Client {
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
//模仿多jvm
new OrderService().getOrderNum();
},String.valueOf(i)).start();
}
}
}
输出:
19 占用锁成功。
获得编号:----->: 生成订单号:1
19 释放锁成功。
12 占用锁成功。
获得编号:----->: 生成订单号:2
12 释放锁成功。
9 占用锁成功。
获得编号:----->: 生成订单号:3
9 释放锁成功。
7 占用锁成功。
获得编号:----->: 生成订单号:4
0 占用锁成功。
获得编号:----->: 生成订单号:5
7 释放锁成功。
0 释放锁成功。
17 占用锁成功。
获得编号:----->: 生成订单号:6
17 释放锁成功。
4 占用锁成功。
获得编号:----->: 生成订单号:7
4 释放锁成功。
6 占用锁成功。
获得编号:----->: 生成订单号:8
6 释放锁成功。
14 占用锁成功。
获得编号:----->: 生成订单号:9
14 释放锁成功。
11 占用锁成功。
获得编号:----->: 生成订单号:10
11 释放锁成功。
10 占用锁成功。
获得编号:----->: 生成订单号:11
10 释放锁成功。
2 占用锁成功。
获得编号:----->: 生成订单号:12
2 释放锁成功。
18 占用锁成功。
获得编号:----->: 生成订单号:13
18 释放锁成功。
15 占用锁成功。
获得编号:----->: 生成订单号:14
15 释放锁成功。
13 占用锁成功。
获得编号:----->: 生成订单号:15
13 释放锁成功。
8 占用锁成功。
获得编号:----->: 生成订单号:16
8 释放锁成功。
16 占用锁成功。
获得编号:----->: 生成订单号:17
16 释放锁成功。
5 占用锁成功。
获得编号:----->: 生成订单号:18
5 释放锁成功。
3 占用锁成功。
获得编号:----->: 生成订单号:19
3 释放锁成功。
1 占用锁成功。
获得编号:----->: 生成订单号:20
1 释放锁成功。