1:多线程

  java语言的特点在于多线程,当然优势下就会出现相对应的挑战.在多线程环境下,线程安全成为一个开发者必须要避免的问题.当一个线程共享一个变量的时候,根据内存模型,每个线程都有自己的本地内存,共享变量的内存放在主内存中.当然如果是读取,肯定不会产生线程安全问题,现在有多个线程同时对一个变量进行操作的时候,那么变量到底是听谁的呢?先上代码:

run的结果为

 加了concurrent包下的lock锁之后单机的多线程问题暂时解决

那么问题来了 在分布式的大环境下,不同的jvm,锁的问题该如何解决呢?

在此想到计算界的一句名言:计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决

而本次的主题,zookeeper就是这个间接的中间层,上篇介绍了zookeeper的特性,zookeeper作为分布式锁确实有它独到的优势,目前从市面上了解的分布式锁实现方案(了解有限)

1:使用数据库做分布式锁,用数据库作为分布式锁的缺点显而易见

2:使用redis做分布式锁,而使用redis的缺点是一旦服务器挂了锁来不及释放,虽然可以设置过期时间,但是也成为一个该方式的一个瓶颈

Zookeeper实现分布式锁的好处/方案可行性

1:每一个节点只能创建一次,创建节点的过程就是获取锁的过程

2:zk的监听机制,监听到节点的删除(锁释放),通知给等待锁的线程

3:如果获取锁了的服务器节点宕机,zk只要连接中断,就是自动删除该节点(弥补redis的不足)

实现分布式锁使用的zookeeper的特性

1:同path的节点有且只有一个

2:删除节点,可以重新创建

3:事件通知,可以监听指定节点状态(删除,节点数据改变)

实现分布式锁的步骤:

一.对外开放两个接口方法,获取锁,释放锁

二.获取锁步骤:1.尝试获取锁

        2.步骤一成功则继续往下运行;失败则阻塞线程等待被唤醒,被唤醒之后,继续尝试获取锁

三.释放锁,直接释放锁资源

代码实现:

public abstract class AbstractZkLock implements ZkLockInterface {
    //zk连接信息
    private static final String SERVERSTRING = "127.0.0.1:2181";
    //节点信息
    protected static final String LOCK_PATH = "/zkLockPath";
    //客户端连接
    ZkClient zkClient = new ZkClient(SERVERSTRING);


    @Override
    public void getLock()  {
        if (tryLock()) {
            System.out.println("---------------获取锁资源---------------");
        } else {
            //等待锁  阻塞操作 被唤醒说明锁已经释放
            waitLock();
            //锁释放后重新获取锁
            getLock();
        }

    }

    //等待锁资源
    abstract void waitLock() ;

    //尝试获取锁资源
    abstract boolean tryLock();


    @Override
    public void unLock() {
        zkClient.close();
    }


}

上图为基本的锁操作逻辑,主要操作zk实现代码:

public class DistributedZkLock extends AbstractZkLock{

    CountDownLatch countDownLatch = null;

    @Override
    void waitLock() {
        //监听器
        IZkDataListener iZkDataListener = new IZkDataListener() {
            @Override
            public void handleDataChange(String dataPath, Object data) throws Exception {

            }

            @Override
            public void handleDataDeleted(String dataPath) throws Exception {
                //检测到节点删除  则
                if(countDownLatch != null){
                    countDownLatch.countDown();
                }
            }
        };
        //注册事件监听
        zkClient.subscribeDataChanges(LOCK_PATH,iZkDataListener);

        if(zkClient.exists(LOCK_PATH)) {
       //路径存在开启信号量 countDownLatch = new CountDownLatch(1); try {
         //信号量阻塞 等待事件通知唤醒 countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); throw new RuntimeException(e.getMessage()); } } //阻塞结束 删除事件通知 zkClient.unsubscribeDataChanges(LOCK_PATH,iZkDataListener); } @Override boolean tryLock() { try {
       //创建成功则返回true zkClient.createEphemeral(LOCK_PATH); return true; }catch (Exception e){
       //创建失败抛出异常返回false return false; } } }