分布锁的解决方案

分布锁的要求:

a:互斥

b:宕机避免死锁

c:只能自己解锁

1.数据库版本

2.redis

3.zookeeper

4.pom文件

I...依赖

<!-- 原生zk支持-->
 <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.3.6</version>
        </dependency>
<!-- zkclient原生客户端支持-->
        <dependency>
            <groupId>com.github.sgroschupf</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.1</version>
        </dependency>

II...必要参数采用yml配置注入

zklock:
  url: 127.0.0.1:2181
  rootPath: /newlock17

III...创建锁工具类

 要点介绍:

  1.实现lock接口,开闭原则

  2.构造函数中实例化ZkClient

  3.在根节点下常见临时顺序节点记得加分隔符“/”:

package com.test.domi.common.utils.lock;

import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

public class ZKlock implements Lock {

    private ZkClient zkClient;
    private String rootPath;
    private String currentPath;
    private String beforePath;

    public ZKlock(String url,String rootPath){
        this.rootPath = rootPath;
        zkClient = new ZkClient(url);
        if (!zkClient.exists(rootPath)) {
            try {
                zkClient.createPersistent(rootPath);
            } catch (RuntimeException e) {
            }
        }
    }

    @Override
    public boolean tryLock() {
        if (currentPath == null) {
            currentPath = zkClient.createEphemeralSequential(rootPath + "/","aaa");
        }
        List<String> childrens = zkClient.getChildren(rootPath);
        Collections.sort(childrens);
        if (currentPath.equals(rootPath + "/" + childrens.get(0))) {
            return true;
        }else{
            int curIndex = childrens.indexOf(currentPath.substring(rootPath.length() + 1));
            beforePath = rootPath + "/" + childrens.get(curIndex - 1);
        }
        return false;
    }

    @Override
    public void lock() {
        if (!tryLock()) {
            waiForLock();
            lock();
        }
    }

    @Override
    public void unlock() {
        zkClient.delete(currentPath);
    }

    private void waiForLock(){
        CountDownLatch cdl = new CountDownLatch(1);
        IZkDataListener listener = new IZkDataListener() {
            @Override
            public void handleDataChange(String s, Object o) throws Exception {
            }
            @Override
            public void handleDataDeleted(String s) throws Exception {
                cdl.countDown();
            }
        };
        zkClient.subscribeDataChanges(beforePath,listener);
        if (zkClient.exists(beforePath)) {
            try {
                cdl.await();
            } catch (InterruptedException e) {
            }
        }
        zkClient.unsubscribeDataChanges(beforePath,listener);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    @Override
    public Condition newCondition() {
        return null;
    }

}
currentPath = zkClient.createEphemeralSequential(rootPath + "/","aaa");

IV...将锁工具类交给spring管理,这样就不需要每次new的时候传入客户端的连接ip,一次注入终生受用,

但是切记@Scope为多例,因为ZKLock里面的全局变量不能再多线程共享。

package com.test.domi.config;

import com.test.domi.common.utils.lock.ZKlock;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

@Configuration
public class ZkLockConfig {

    @Value("${zklock.rootPath}")
    private String rootPath;
    @Value("${zklock.url}")
    private String url;

    @Bean
    @Scope("prototype")
    public ZKlock getZKlock(){
        return new ZKlock(url,rootPath);
    }
}

V...使用方式:

要点:

  1.zklock.unlock():一般要放在finally中

  2.重入之后一般需要判断业务是否执行过,已经执行过的应立即退出

package com.test.domi.controller;

import com.test.domi.common.utils.SpringContextUtil;
import com.test.domi.common.utils.lock.ZKlock;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/zk")
public class ZKController {

    private int k = 1;

    @GetMapping("/lock")
    public Boolean getLock() throws Exception{


        for (int i = 0; i < 50; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                  ZKlock zklock = SpringContextUtil.getBean(ZKlock.class);
                  zklock.lock();
                    try {
                        System.out.println(Thread.currentThread().getName() + "获得锁,生成唯一的单据编号" + k++);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }finally {
                        zklock.unlock();
                    }
                }
            }).start();
        }

        return true;
    }
}
原文地址:https://www.cnblogs.com/blacksmallcat/p/10155944.html