关于接口幂等性与锁的学习笔记

如何保证接口幂等性:
1,查询与删除操作是天然幂等操作。
2,新增时,根据某个字段做唯一性判断,比如某个用户只会有一条记录这样的类似判断。(实用性差)
3,悲观锁,select from where 主键=’’ for update
4,乐观锁,update user set money = money + 1 where id=2 and money = 1;在字段自加一的时候,加上该字段的等值索引
3与4,如果没有加主键id做索引,会锁表,会影响该表其他功能。

【上诉两种不好用,并发会使程序报错,如同时要买掉一件商品,一件没扣成功则进程失去反应】

5,分布式锁:使用Redis、Memcached、Zookeeper、Chubby等第三方程序

Redis:
①set(key, value, “NX”,“EX”,internalLockLeaseTime):使用set设置键值,以“id_后缀”等规范作为key,以当前线程id作为value。插入一条记录,并设置锁超时时间30秒。
如果插入成功,返回1,以此声明成功获得锁,其他进程插入返回0说明该key已存在,获取锁失败。成功则进入插入或者更新操作。
②释放锁,为了避免误删其他线程的锁,删除时增加判断if(threadId .equals(redisClient.get(key))){ del(key) }删除键值对,del(id_后缀)
③守护线程,同线程只要不崩溃,会一直为其增加有效期,如果崩了,则不会增加,redis到时间自动释放

实操:
一个商品多人同时购买,同时触发buyCommodity()方法,两个进程并发,此时,上锁对象为商品(判断清楚对象很重要,涉及到key的设计,处理并发),即,key值为商品id+"_buy_lock",此时,A线程比B线程提前得到锁(提前set好了),那么B线程set失败,进入循环获取,持续时间内没有获取到,则视为失败。

本人封装一个redis锁工具,并展示测试类:

package com.fjhb;

import redis.clients.jedis.Jedis;

/**
 * redis锁
 * Author:FangKunSen
 * Time:2020-05-29,15:35
 */
public class JLock {
    private String HOST;
    Jedis jedis;
    protected long internalLockLeaseTime = 30;//锁过期时间
    private long timeout = 20000; //获取锁的超时时间
    private long SLEEP = 2000;//休息2秒重新获取

    public JLock(String HOST) {
        this.HOST = HOST;
        jedis = new Jedis(HOST);
    }

    /**
     * 上锁
     * @param key 建议以上锁对象id为标识,加以识别后缀,例如 123456_commodityId_lock
     * @param value 以线程id为值,防止解锁时解到其他线程的锁
     * @return
     */
    public boolean lock(String key,String value){
        Long start = System.currentTimeMillis();
        try{
            for(;;){
                //SET命令返回OK ,则证明获取锁成功
                String lock = jedis.set(key, value, "NX","EX",internalLockLeaseTime);
                if("OK".equals(lock)){
                    return true;
                }
                System.out.println("未取到锁,正在重新取锁...");
                //否则循环等待,在timeout时间内仍未获取到锁,则获取失败
                long l = System.currentTimeMillis() - start;
                if (l>=timeout) {
                    System.out.println("规定时间内未取到锁,失败");
                    return false;
                }
                try {
                    Thread.sleep(SLEEP);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }finally {
            jedis.close();
        }
    }

    /**
     * 获取锁值,校验用,目前值为进程id
     * @param key 建议以上锁对象id为标识,加以识别后缀,例如 123456_commodityId_lock
     * @return
     */
    public String get(String key){
        return jedis.get(key);
    }

    /**
     * 解指定线程id的锁
     * @param key 建议以上锁对象id为标识,加以识别后缀,例如 123456_commodityId_lock
     * @param value 以线程id为值,防止解锁时解到其他线程的锁
     * @return
     */
    public boolean unLock(String key, String value){
        String threadId=get(key);
        if(threadId.equals(value)){
            jedis.del(key);
            System.out.println("解锁成功,下个继续");
            return true;
        }
        System.out.println("解锁失败");
        return false;
    }
}

	@Test
    public void test() {
        JLock jLock = new JLock("127.0.0.1");
        String key="10101010_commodityId";
        String threadId = String.valueOf(Thread.currentThread().getId());
        if(jLock.lock(key,threadId)){
            System.out.println("取锁成功,执行代码,线程id:"+threadId);
            for(int i = 0;i<10;i++){
                System.out.println(i+1);
            }
            System.out.println("代码执行完毕,正在关闭锁");
            jLock.unLock(key,threadId);
        }else{
            System.out.println("超时未取到");
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值