import com.itmayiedu.lock.ExtLock;
import com.itmayiedu.lock.ZookeeperDistrbuteLock;
public class OrderService implements Runnable{
private OrderNumGenerator orderNumGenerator = new OrderNumGenerator();
private ExtLock lock = new ZookeeperDistrbuteLock();
public void run() {
getNumber();
}
// 使用 synchronized 目的保证线程安全 , 只能让一个线程进行操作
public synchronized void getNumber(){
try {
lock.getLock();
String number = orderNumGenerator.getNumber();
System.out.println(Thread.currentThread().getName() + ",number:" + number);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
lock.unLock();
}
}
// 如果服务器是单机版,生成订单号可以使用 synchronized 或是 lock保证线程问题
public static void main(String[] args) {
//多个线程共享同一个全局 id
// OrderService orderService = new OrderService();
// 模拟分布式锁的场景
for(int i = 0; i < 100; i++){
new Thread(new OrderService()).start();
}
}
}
import java.text.SimpleDateFormat;
import java.util.Date;
//生成订单号 使用时间戳
public class OrderNumGenerator {
// 区分不同的订单号
private static int count = 0;
// 单台服务器上,多个线程同时 生成订单号 线程安全
public String getNumber(){
try {
Thread.sleep(200);
} catch (Exception e) {
e.printStackTrace();
}
SimpleDateFormat simpt = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
return simpt.format(new Date()) + "-" + ++count;
}
}
public interface ExtLock {
//ExtLock 基于zk实现分布式锁
//获取锁
public void getLock();
//释放锁
public void unLock();
}
import java.util.concurrent.CountDownLatch;
import org.I0Itec.zkclient.ZkClient;
//将重复代码抽象到子类中(设计模式模板方法设计模式)
public abstract class ZookeeperAbstractLock implements ExtLock{
private static final String CONNECTION = "127.0.0.1:2181";
protected ZkClient zkClient = new ZkClient(CONNECTION);
protected String lockPath = "/lockPath";
protected CountDownLatch countDownLatch = null;
//获取锁
public void getLock() {
// 1.连接zkClient 在zk上创建一个 /lock节点,节点类型为临时节点
// 2. 如果节点创建成功 直接执行业务逻辑,如果节点创建失败,进行等待
if(tryLock()){
System.out.println("############获取锁资源成功.............###########");
}else{
// 进行等待
// 3. 使用事件通知监听该节点是否被删除,如果被删除的话,重新进入获取锁的资源
waitLock();
getLock();
}
}
//如果节点创建失败,进行等待 使用事件通知监控该节点是否被删除,如果被删除的话,重新进入获取锁的资源
abstract void waitLock();
// 获取锁的资源 如果能够获取锁,成功返回 true 否则返回 false
abstract boolean tryLock();
//释放锁
public void unLock() {
// 当程序执行完毕的时候,直接关闭连接
if(zkClient != null){
zkClient.close();
System.out.println("#################释放锁成功...############");
}
}
}
import java.util.concurrent.CountDownLatch;
import org.I0Itec.zkclient.IZkDataListener;
public class ZookeeperDistrbuteLock extends ZookeeperAbstractLock{
@Override
void waitLock() {
IZkDataListener iZkDataListener = new IZkDataListener() {
//节点被删除
public void handleDataDeleted(String dataPath) throws Exception {
if (countDownLatch != null) {
countDownLatch.countDown(); // 计数器一旦为0的情况,继续向下执行
}
}
//节点被修改
public void handleDataChange(String dataPath, Object data) throws Exception {
// TODO Auto-generated method stub
}
};
//监听事件通知
zkClient.subscribeDataChanges(lockPath, iZkDataListener);
// 如果控制程序等待
if(zkClient.exists(lockPath)){
countDownLatch = new CountDownLatch(1);
try {
countDownLatch.await();
} catch (Exception e) {
}
}
// 后面代码继续执行
// 为了不影响程序的效率, 建议删除该事件监听
zkClient.unsubscribeDataChanges(lockPath, iZkDataListener);
}
@Override
boolean tryLock() {
try {
zkClient.createEphemeral(lockPath);
return true;
} catch (Exception e) {
//如果创建该节点失败的话,这个之后直接 catch
return false;
}
}
}
执行OrderService的main方式模拟实现分布式锁