加入收藏 | 设为首页 | 会员中心 | 我要投稿 汽车网 (https://www.0577qiche.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 教程 > 正文

Java幂等性与分布式锁如何理解

发布时间:2023-04-22 13:45:04 所属栏目:教程 来源:
导读:本篇内容主要讲解“Java幂等性与分布式锁怎么理解”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java幂等性与分布式锁怎么理解”吧!

1. 什
本篇内容主要讲解“Java幂等性与分布式锁怎么理解”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java幂等性与分布式锁怎么理解”吧!

1.  什么是幂等性
幂等性就是指:一个幂等操作任其执行多次所产生的影响均与一次执行的影响相同。用数学的概念表达是这样的: f(f(x)) = f(x).就像 nx1 = n 一样, x1 就是一个幂等操作。无论是乘以多少次结果都一样。

2.  常见的幂等性问题
幂等性问题经常会是由网络问题引起的,还有重复操作引起的。

示例代码:

public void like(Article article,User user) {    //检查是否点过赞
    if (checkIsLike(article,user)) {        //点过赞了
        throw new ApiException(CodeEnums.SYstem_ERR);
    } else {        //保存点赞
        saveLike(article,user);
    }
}
</pre>
看上去好像没有什么问题,保存点赞之前已经检查过是否点赞了,理论上同一个人不会对同一篇文章重复点赞。但实际不是这样的。因为网络请求不是排队进来的,而是一窝蜂涌进来的。

某些时候,用户网络不好,可能很短的时间内点击了多次,由于网络传输问题,这些请求可能会同时来到我们的服务器。

第一个请求 checkIsLike() 返回 false , 正在执行 saveLike() 操作,还没来的及提交事务

第二个请求过来了 ,checkIsLike() 返回 也是 false , 并去 执行了 saveLike() 操作

这样子,就造成了一个用户同时对一篇文章进行了多次点赞操作。

这就是典型的幂等性问题, 操作了一次和操作了两次结果不一样,因为你多点了一次赞,按照幂等性原则 不管你点击了多少次结果都一样,只点了一次赞。

很多场景都是这样造成的,比如用户重复下单,重复评论,重复提交表单等。

那怎么解决呢?假设网络的请求是排队进来的就不会出现这个问题了。

于是我们可以改成这样:

public synchronized void like(Article article,User user) {    //检查是否点过赞
    if (checkIsLike(article,user)) {        //点过赞了
        throw new ApiException(CodeEnums.SYstem_ERR);
    } else {        //保存点赞
        saveLike(article,user);
    }
}
</pre>
synchronized 同步锁 这样我们的请求就会乖乖的排队进来了。

PS :这样做是效率比较低的做法,不建议这么做,只是举例子,synchronized 也不适合分布式集群场景。

我们系统经常需要和第三方系统打交道,比如微信充值,支付宝充值什么的,微信和支付宝常常会以回调你的接口通知你支付结果。为了保证你能收到回调,往往可能会回调多次。

有时候我们也为了保证数据的准确性会有个定时器去查询支付结果未知的流水,并执行响应的处理。
如果定时器的轮训和回调刚好是在同时进行,这可能又出BUG了,又进行了两次重复操作。

那么问题来了:假设我是一个充值操作, 回调回来的时候 ,会做业务处理,成功了给用户账户加钱。这是后就要保证幂等性了, 假设微信同一笔交易给你回调了两次,如果你给用户充值了两次,这显然不合理(我是老板肯定扣你工资),所以要保证 不管微信回调你多少次 ,同一笔交易你只能给用户充一次钱。这就幂等性。

解决幂等性问题方案
synchronized 适合单机应用,不追求性能 ,不追求并发。

分布式锁 但是往往我们的应用是分布式的集群,并且很讲究性能,并发,所以我们需要用到 分布式锁 来解决这个问题。

Redis 分布式锁:

/**
* setNx
*
*  @param key
*  @param value
*  @return*/public Boolean setNx(String key,Object value) {    return redistemplate.opsForValue().setIfAbsent(key,value);
}/**
*  @param key 锁
*  @param waitTime 等待时间  毫秒
*  @param expireTime 超时时间  毫秒
*  @return*/public Boolean lock(String key,long waitTime,long expireTime) {
    String vlaue =  UUIDUtil.mongoObjectId();
    Boolean flag = setNx(key,vlaue);    //尝试获取锁  成功返回
    if (flag) {
        redistemplate.expire(key,expireTime,TimeUnit.MILLISECONDS);        return flag;
    } else {        //失败
        //现在时间
        long newTime =  System.currentTimeMillis();        //等待过期时间
        long loseTime = newTime + waitTime;        //不断尝试获取锁成功返回
        while (System.currentTimeMillis()  < loseTime) {
            Boolean testFlag = setNx(key,vlaue);            if (testFlag) {
                redistemplate.expire(key,expireTime,TimeUnit.MILLISECONDS);                return testFlag;
            }            //休眠100毫秒
            try {
                Thread.sleep(100);
            }            catch (InterruptedException e) {
                e.printstacktrace();
            }
        }
    }    return false;
}/**
*  @param key
*  @return*/public Boolean lock(String key) {    return lock(key,1000L,60  *  1000L);
}/**
*  @param key
*/public void unLock(String key) {
    remove(key);
}
</pre>

(编辑:汽车网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章