Redis实现分布式锁

场景介绍

很多互联网场景(如商品秒杀,论坛回帖盖楼等),需要用加锁的方式,以对某种资源进行顺序访问控制。如果应用服务集群部署,则涉及到对分布式应用加锁。当前分布式加锁主要有三种方式:(磁盘)数据库、缓存数据库、Zookeeper。接下里让我们一起看看加锁实践过程。

加锁实现

/**
 * @author ZF
 * @Description TODO
 * @Date 2020/2/22 12:21
 */
public class DistributedLock {

    private final String host = "192.168.16.3";

    private final int port = 6379;


    private static final String SUCCESS = "OK";

    private static final String SET_IF_NOT_EXIST = "NX";

    private static final String EXPIRE_TIME = "PX";


    public DistributedLock() {
    }



    /*
     * @param lockName      锁名
     * @param timeout       获取锁的超时时间
     * @param lockTimeout   锁的有效时间
     * @return              锁的标识

     */

    public String getLockWithTimeout(String lockName, long timeout, long lockTimeout) {

        String ret = null;
        Jedis jedisClient = new Jedis(host, port);


        try {
            String authMsg = jedisClient.auth("123456");

            if (!SUCCESS.equals(authMsg)) {

                System.out.println("AUTH FAILED: " + authMsg);

            }


            String lockKey = "DLock:" + lockName;

            String identifier = UUID.randomUUID().toString();


            long end = System.currentTimeMillis() + timeout;


            while (System.currentTimeMillis() < end) {

                String result = jedisClient.set(lockKey, identifier, SET_IF_NOT_EXIST, EXPIRE_TIME, lockTimeout);

                if (SUCCESS.equals(result)) {

                    ret = identifier;

                    break;

                }


                try {

                    Thread.sleep(2);

                } catch (InterruptedException e) {

                    Thread.currentThread().interrupt();

                }

            }

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            jedisClient.quit();

            jedisClient.close();

        }


        return ret;

    }

    /*

     * @param lockName        锁名

     * @param identifier    锁的标识

     */

    public void releaseLock(String lockName, String identifier) {

        Jedis jedisClient = new Jedis(host, port);


        try {

            String authMsg = jedisClient.auth("123456");

            if (!SUCCESS.equals(authMsg)) {

                System.out.println("AUTH FAILED: " + authMsg);

            }


            String lockKey = "DLock:" + lockName;

            if (identifier.equals(jedisClient.get(lockKey))) {

                jedisClient.del(lockKey);

            }

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            jedisClient.quit();

            jedisClient.close();
       	 }
  	  }
	}
}

测试代码

假设20个线程对10台mate10手机进行抢购:

public class CaseTest {

    public static void main(String[] args) {

        ServiceOrder service = new ServiceOrder();

        for (int i = 0; i < 20; i++) {

            ThreadBuy client = new ThreadBuy(service);

            client.start();

        }

    }

}

 

class ServiceOrder {

    private final int MAX = 10;

 

    DistributedLock DLock = new DistributedLock();

 

    int n = 10;

 

    public void handleOder() {

        String userName = UUID.randomUUID().toString().substring(0,8) + Thread.currentThread().getName();

        String identifier = DLock.getLockWithTimeout("Huawei Mate 10", 10000, 2000);

        System.out.println("正在为用户:" + userName + " 处理订单");

        if(n > 0) {

            int num = MAX - n + 1;

            System.out.println("用户:"+ userName + "购买第" + num + "台,剩余" + (--n) + "台");

        }else {

            System.out.println("用户:"+ userName + "无法购买,已售罄!");

        }

        DLock.releaseLock("Huawei Mate 10", identifier);

    }

}

 

class ThreadBuy extends Thread {

    private ServiceOrder service;

 

    public ThreadBuy(ServiceOrder service) {

        this.service = service;

    }

 

    @Override

    public void run() {

        service.handleOder();

    }

}

运行结果

配置好实际的缓存实例连接地址、端口与连接密码,运行代码,得到以下结果:

正在为用户:eee56fb7Thread-16 处理订单

用户:eee56fb7Thread-16购买第1台,剩余9台

正在为用户:d6521816Thread-2 处理订单

用户:d6521816Thread-2购买第2台,剩余8台

正在为用户:d7b3b983Thread-19 处理订单

用户:d7b3b983Thread-19购买第3台,剩余7台

正在为用户:36a6b97aThread-15 处理订单

用户:36a6b97aThread-15购买第4台,剩余6台

正在为用户:9a973456Thread-1 处理订单

用户:9a973456Thread-1购买第5台,剩余5台

正在为用户:03f1de9aThread-14 处理订单

用户:03f1de9aThread-14购买第6台,剩余4台

正在为用户:2c315ee6Thread-11 处理订单

用户:2c315ee6Thread-11购买第7台,剩余3台

正在为用户:2b03b7c0Thread-12 处理订单

用户:2b03b7c0Thread-12购买第8台,剩余2台

正在为用户:75f25749Thread-0 处理订单

用户:75f25749Thread-0购买第9台,剩余1台

正在为用户:26c71db5Thread-18 处理订单

用户:26c71db5Thread-18购买第10台,剩余0台

正在为用户:c32654dbThread-17 处理订单

用户:c32654dbThread-17无法购买,已售罄!

正在为用户:df94370aThread-7 处理订单

用户:df94370aThread-7无法购买,已售罄!

正在为用户:0af94cddThread-5 处理订单

用户:0af94cddThread-5无法购买,已售罄!

正在为用户:e52428a4Thread-13 处理订单

用户:e52428a4Thread-13无法购买,已售罄!

正在为用户:46f91208Thread-10 处理订单

用户:46f91208Thread-10无法购买,已售罄!

正在为用户:e0ca87bbThread-9 处理订单

用户:e0ca87bbThread-9无法购买,已售罄!

正在为用户:f385af9aThread-8 处理订单

用户:f385af9aThread-8无法购买,已售罄!

正在为用户:46c5f498Thread-6 处理订单

用户:46c5f498Thread-6无法购买,已售罄!

正在为用户:935e0f50Thread-3 处理订单

用户:935e0f50Thread-3无法购买,已售罄!

正在为用户:d3eaae29Thread-4 处理订单

用户:d3eaae29Thread-4无法购买,已售罄!

不加锁场景
如果注释掉加锁代码,变成无锁情况,则抢购无序。

//测试类中注释两行用于加锁的代码:
public void handleOder() {
    String userName = UUID.randomUUID().toString().substring(0,8) + Thread.currentThread().getName();
    //加锁代码
    //String identifier = DLock.getLockWithTimeout("Huawei Mate 10", 10000, 2000);
    System.out.println("正在为用户:" + userName + " 处理订单");
    if(n > 0) {
        int num = MAX - n + 1;
        System.out.println("用户:"+ userName + "够买第" + num + "台,剩余" + (--n) + "台");
    }else {
        System.out.println("用户:"+ userName + "无法够买,已售罄!");
    }

    //加锁代码
    //DLock.releaseLock("Huawei Mate 10", identifier);
}

注释加锁代码后的运行结果,可以看出处理过程是无序的:

正在为用户:e04934ddThread-5 处理订单 正在为用户:a4554180Thread-0 处理订单
用户:a4554180Thread-0购买第2台,剩余8台 正在为用户:b58eb811Thread-10 处理订单
用户:b58eb811Thread-10购买第3台,剩余7台 正在为用户:e8391c0eThread-19 处理订单
正在为用户:21fd133aThread-13 处理订单 正在为用户:1dd04ff4Thread-6 处理订单
用户:1dd04ff4Thread-6购买第6台,剩余4台 正在为用户:e5977112Thread-3 处理订单
正在为用户:4d7a8a2bThread-4 处理订单 用户:e5977112Thread-3购买第7台,剩余3台
正在为用户:18967410Thread-15 处理订单 用户:18967410Thread-15购买第9台,剩余1台
正在为用户:e4f51568Thread-14 处理订单 用户:21fd133aThread-13购买第5台,剩余5台
用户:e8391c0eThread-19购买第4台,剩余6台 正在为用户:d895d3f1Thread-12 处理订单
用户:d895d3f1Thread-12无法购买,已售罄! 正在为用户:7b8d2526Thread-11 处理订单
用户:7b8d2526Thread-11无法购买,已售罄! 正在为用户:d7ca1779Thread-8 处理订单
用户:d7ca1779Thread-8无法购买,已售罄! 正在为用户:74fca0ecThread-1 处理订单
用户:74fca0ecThread-1无法购买,已售罄! 用户:e04934ddThread-5购买第1台,剩余9台
用户:e4f51568Thread-14购买第10台,剩余0台 正在为用户:aae76a83Thread-7 处理订单
用户:aae76a83Thread-7无法购买,已售罄! 正在为用户:c638d2cfThread-2 处理订单
用户:c638d2cfThread-2无法购买,已售罄! 正在为用户:2de29a4eThread-17 处理订单
用户:2de29a4eThread-17无法购买,已售罄! 正在为用户:40a46ba0Thread-18 处理订单
用户:40a46ba0Thread-18无法购买,已售罄! 正在为用户:211fd9c7Thread-9 处理订单
用户:211fd9c7Thread-9无法购买,已售罄! 正在为用户:911b83fcThread-16 处理订单
用户:911b83fcThread-16无法购买,已售罄! 用户:4d7a8a2bThread-4购买第8台,剩余2台

总的来说,使用DCS服务中Redis类型的缓存实例实现分布式加锁,有几大优势:

1、加锁操作简单,使用SET、GET、DEL等几条简单命令即可实现锁的获取和释放。

2、性能优越,缓存数据的读写优于磁盘数据库与Zookeeper。

3、可靠性强,DCS有主备和集群实例类型,避免单点故障。

以上代码实现仅展示使用DCS服务进行加锁访问的便捷性.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值