1.jdk锁
lock、synchronized、reentracklock等jdk自带锁,只能在同一个jvm中起作用,无法解决分布式多任务竞争共享资源的问题
2. 分布式锁对比
image.png3.模板方法实现
tryLock,waitLock,unLock
4.获取锁流程
image.png5.代码实现
- 创建锁类(获取、阻塞、释放锁)
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
* @Description: zk获取、阻塞、释放锁
* @Author: liangyunbo
* @Date: 2019/11/20 9:30 AM
*/
public class ZookeeperDistrbuteLock_ {
private CountDownLatch countDownLatch= null;
// zk连接地址
private static final String CONNECTSTRING = "172.16.223.132:2181";
// 创建zk连接
protected ZkClient zkClient = new ZkClient(CONNECTSTRING);
//设置路径
protected static final String PATH2 = "/lock";
//当前请求的节点前一个节点
private String beforePath;
//当前请求的节点
private String currentPath;
public ZookeeperDistrbuteLock_() {
if (!this.zkClient.exists(PATH2)) {
this.zkClient.createPersistent(PATH2);
}
}
/**
* @Description: 获取锁
* @Param: []
* @Return: void
* @Author: liangyunbo
* @Date: 2019/11/20 1:43 PM
*/
public void getLock() {
if (tryLock()) {
System.out.println("##获取lock锁的资源####");
} else {
waitLock();
getLock();
}
}
/**
* @Description: 是否获取到锁
* @Param: []
* @Return: void
* @Author: liangyunbo
* @Date: 2019/11/20 9:57 AM
*/
public boolean tryLock() {
//如果currentPath为空则为第一次尝试加锁,第一次加锁赋值currentPath
if(currentPath == null || currentPath.length()<= 0){
//创建一个临时顺序节点
currentPath = this.zkClient.createEphemeralSequential(PATH2 + '/',"lock");
}
//获取所有临时节点并排序,临时节点名称为自增长的字符串如:0000000400
List<String> childrens = this.zkClient.getChildren(PATH2);
Collections.sort(childrens);
if (currentPath.equals(PATH2 + '/'+childrens.get(0))) {//如果当前节点在所有节点中排名第一则获取锁成功
return true;
} else {//如果当前节点在所有节点中排名中不是排名第一,则获取前面的节点名称,并赋值给beforePath
int wz = Collections.binarySearch(childrens,
currentPath.substring(PATH2.length()+1));
beforePath = PATH2 + '/'+childrens.get(wz-1);
}
return false;
}
/**
* @Description: 阻塞锁
* @Param: []
* @Return: void
* @Author: liangyunbo
* @Date: 2019/11/20 1:39 PM
*/
public void waitLock() {
IZkDataListener listener = new IZkDataListener() {
public void handleDataDeleted(String dataPath) throws Exception {
if(countDownLatch!=null){
countDownLatch.countDown();
}
}
public void handleDataChange(String dataPath, Object data) throws Exception {
}
};
//给排在前面的的节点增加数据删除的watcher,本质是启动另外一个线程去监听前置节点
this.zkClient.subscribeDataChanges(beforePath, listener);
if(this.zkClient.exists(beforePath)){
countDownLatch=new CountDownLatch(1);
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.zkClient.unsubscribeDataChanges(beforePath, listener);
}
/**
* @Description: 释放锁
* @Param: []
* @Return: void
* @Author: liangyunbo
* @Date: 2019/11/20 1:40 PM
*/
public void unLock() {
zkClient.delete(currentPath);
zkClient.close();
}
}
- 调用类
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Description: 调用类
* @Author: liangyunbo
* @Date: 2019/11/20 2:33 PM
*/
public class OrderService_ implements Runnable {
//全局订单id
public static int count = 0;
private ZookeeperDistrbuteLock_ lock = new ZookeeperDistrbuteLock_();
/**
* @Description: main
* @Author: liangyunbo
* @Date: 2019/11/20 2:29 PM
*/
public static void main(String[] args) {
System.out.println("####生成唯一订单号###");
for (int i = 0; i < 50; i++) {
new Thread( new OrderService_()).start();
}
}
public void run() {
try {
lock.getLock();
String number = getNumber();
System.out.println(Thread.currentThread().getName() + ",生成订单ID:" + number);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unLock();
}
}
/**
* @Description: 生成订单id
* @Author: liangyunbo
* @Date: 2019/11/20 2:15 PM
*/
public String getNumber() {
SimpleDateFormat simpt = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
return simpt.format(new Date()) + "-" + ++count;
}
}
网友评论