基于Zookeeper的分布式锁

01.分布式锁

1.1 为什么使用分布式锁

一个方法在高并发情况下的同一时间只能被同一个线程执行,在传统单体应用单机部署的情况下,可以使用Java并发处理相关的API(如ReentrantLcok或synchronized)进行互斥控制。但是,随着业务发展的需要,原单体单机部署的系统被演化成分布式系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题.
在这里插入图片描述

1.2 基于zookeeper的分布式锁原理

一个分布式锁对应ZooKeeper的一个节点,每个需要获取这个分布式锁的客户端线程在这个节点下创建一个临时有序节点,此时有两种情况:

  1. 创建的临时顺序节点是文件夹下的第一个节点,则认为是获取分布式锁成功。

  2. 创建的临时顺序节点不是文件夹下的第一个节点,则认为当前锁已经被另一个客户端线程获取,此时需要进入阻塞状态,等待节点顺序中的前一个节点释放锁的时候唤醒当前线程

在这里插入图片描述

02 应用场景实战:基于ZK的分布式锁

2.1 核心类

在这里插入图片描述

2.2 实战场景

这里我们模拟修改用户积分(score)的操作,如果多个线程同时修改一个用户的积分就会有并发问题,因此需要加入锁来进行控制。

【详见1.1】

2.3 代码实现
  1. 创建package: com.itheima.study.zk.lock
  2. 创建UserDao,提供getScoreFromDb和updateScore两个方法
  3. 创建DistributedLockDemo类以及main方法
  4. 完善main方法
    1. 实例化zookeeper客户端CuratorFramework
    2. 实例化分布式锁InterProcessLock
    3. 实例化UserDao
    4. 并发执行100遍修改用户积分(score)操作
    5. 输出用户最终的积分(score)
  5. 执行查看结果
  6. 去除代码中锁的逻辑再次执行并查看结果
  • com.itheima.study.zk.lock.UserService
/**
 * 用户DAO层,模拟读写数据库
 */
public class UserDao {
	
    private int score = 0;

    /**
     * 模拟从数据库获取用户积分
     * @return
     */
    public int getScoreFromDb() {
        //模拟网络请求耗时1毫秒
        try {
            Thread.sleep(1L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //查徐数据
        return score;
    }

    /**
     * 模拟更新数据库里的用户积分
     * @param score
     */
    public void updateScore(int score) {
        //模拟网络请求耗时1毫秒
        try {
            Thread.sleep(1L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.score = score;
    }
}

  • com.itheima.study.zk.lock.DistributedLockDemo
 /**
 * curator实现的分布式锁使用实例
 */
public class DistributedLockDemo {

    public static void main(String[] args) throws InterruptedException {
        //构造超时重试策略,每1s重试一次,重试10次
        RetryPolicy retryPolicy = new RetryNTimes(10,1000);
        //构造客户端
        CuratorFramework client = CuratorFrameworkFactory.newClient("localhost:2181",retryPolicy);
        // 启动客户端
        client.start();

        //构造锁
        InterProcessLock lock = new InterProcessMutex(client,"/user/1/update");

        //构造userDao
        UserDao userDao = new UserDao();

        //并发修改100遍
        for (int i = 0; i < 100; i++) {
            new Thread((new Runnable() {
                @Override
                public void run() {
                    try {
                        //lock.acquire();
                        // 查询当前积分
                        int score = userDao.getScoreFromDb();
                        // 积分加1
                        score++;
                        //更新数据库
                        userDao.updateScore(score);
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        try {
                            //lock.release();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            })).start();
        }
        //随眠5s等待任务执行完成
        Thread.sleep(5000L);
        //输出最终的结果
        System.out.println("完成,结果:"+userDao.getScoreFromDb());
           
        //关闭client
        client.close();
    }
}
  • 输出结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yy1ZyM9H-1600261160235)(img/1573978931011.png)]

  • 注释掉代码中获取锁和释放锁的逻辑

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WTeIp2IG-1600261160237)(img/1573978812698.png)]

  • 再次执行,执行的结果就不是100了,而且每次都不同,这就是并发问题导致的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TU2s28Na-1600261160239)(img/1574044589454.png)]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

娃娃 哈哈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值