用zookeeper实现分布式锁,配合模板模式和CountDownLatch

  • Post author:
  • Post category:其他

一、什么是分布式锁

分布式锁是控制分布式系统之间同步访问共享资源的一种方式,在高并发的多台主机之间,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁,

二、常见分布式锁的实现方式

1. MySQL数据库的乐观锁
2. redis redission
3. zookeeper 添加临时节点,用完后再退出

三、分布式锁的实现

3.1 引入zkclient依赖

    <dependency>
        <groupId>com.101tec</groupId>
        <artifactId>zkclient</artifactId>
        <version>0.11</version>
    </dependency>

3.2 抽象类 实现 锁接口

public abstract class ZkAbstractTemplateLock implements ZkLock {

    public static final String ZKSERVER = "192.168.100.1:2181";
    public static final int TIME_OUT = 45 * 1000;
    ZkClient zkClient = new ZkClient(ZKSERVER, TIME_OUT);
    protected String path = "/zklock";
    protected CountDownLatch countDownLatch;

    @Override
    public void zklock() {
        if (tryZkLock()) {
            System.out.println(Thread.currentThread().getName() + "\t 占用锁成功");
        } else {
            waitZkLock();
            zklock();
        }
    }

    public abstract boolean tryZkLock();

    public abstract void waitZkLock();

    @Override
    public void zkUnlock() {
        if (zkClient != null) {
            zkClient.close();
        }
        System.out.println(Thread.currentThread().getName() + "\t 释放锁成功");
        System.out.println();
        System.out.println();

    }
}

3.3 分布式锁继承抽象类

/**
 * @author youfy
 * @date 2020-03-04 22:46分
 */
public class ZkDistributedLock extends ZkAbstractTemplateLock {
    @Override
    public boolean tryZkLock() {

        try{
            zkClient.createEphemeral(path);
            return true;
        }catch (Exception e){
            return false;
        }
    }

    @Override
    public void waitZkLock() {
        //监听器
        IZkDataListener iZkDataListener = new IZkDataListener() {
            @Override
            public void handleDataChange(String s, Object o) throws Exception {

            }

            @Override
            public void handleDataDeleted(String s) throws Exception {
                if(countDownLatch!=null){
                    countDownLatch.countDown();
                }
            }
        };

        zkClient.subscribeDataChanges(path,iZkDataListener);

        if(zkClient.exists(path)){
            //通过countDownLatch实现只能等着,不往下走
            countDownLatch = new CountDownLatch(1);
            try {
                countDownLatch.await();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }

        zkClient.unsubscribeDataChanges(path,iZkDataListener);
    }
}

3.4 锁接口

public interface ZkLock {

    public void zklock();

    public void zkUnlock();
}

3.5 订单服务

public class OrderService {
    private OrderNumCreateUtil orderNumCreateUtil = new OrderNumCreateUtil();
    private ZkLock zkLock = new ZkDistributedLock();

    public void getOrdNumber() {
        zkLock.zklock();
        try {
            System.out.println("获得编号:--->:" + orderNumCreateUtil.getOrdNumber());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            zkLock.zkUnlock();
        }
    }
}

3.6 创建订单号

public class OrderNumCreateUtil {
    private static  int number =0;
    public String getOrdNumber(){
        return "\t 生成订单号:"+(++number);
    }
}

3.7 客户端类实现多线程调用

public class Client {
    public static void main(String[] args) {

        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                new OrderService().getOrdNumber();
            },String.valueOf(i)).start();
        }
    }
}

四、什么是模板模式()

一个抽象类公开定义了执行它的方法的模板,它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行

意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

主要解决:一些方法通用,却在每一个子类都重新写了这一方法

何时使用:有一些通用的方法

如何解决:将这些通用方法抽象出来

关键代码:在抽象类实现,其他步骤在子类实现