分布式锁之redis锁--手写redis

分布式锁,即同一个服务被部署成集群,这样就涉及到资源争抢的问题,单机的资源,直接用jvm内部的synchronized的锁就可以

分布式锁的接口

package com.online.taxi.order.service;


public interface GrabService {

    /**
     * 司机抢单
     * @param orderId
     * @param driverId
     * @return
     */
    public String grabOrder(int orderId , int driverId);
}

 分布式锁的实现类,里面需要注入需要抢占资源的业务类

package com.online.taxi.order.service.impl;

import com.online.taxi.order.service.GrabService;
import com.online.taxi.order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;


@Service("grabRedisLockService")
public class GrabRedisLockServiceImpl implements GrabService {

	@Autowired
	StringRedisTemplate stringRedisTemplate;
	
	@Autowired
	OrderService orderService;
	
    @Override
    public String grabOrder(int orderId , int driverId){
        //生成key
    	String lock = "order_"+(orderId+"");
    	/*
    	 *  情况一,如果锁没执行到释放,比如业务逻辑执行一半,运维重启服务,或 服务器挂了,没走 finally,怎么办?
    	 *  加超时时间  如果不设置超时时间,资源一直被占用,不能被其它人抢用,设置了过期时间,就会释放锁
    	 *  setnx
    	 */
//    	boolean lockStatus = stringRedisTemplate.opsForValue().setIfAbsent(lock.intern(), driverId+"");
//    	if(!lockStatus) {
//    		return null;
//    	}
    	
    	/*
    	 *  情况二:加超时时间,会有加不上的情况,运维重启
    	 */
//    	boolean lockStatus = stringRedisTemplate.opsForValue().setIfAbsent(lock.intern(), driverId+"");
//    	stringRedisTemplate.expire(lock.intern(), 30L, TimeUnit.SECONDS);
//    	if(!lockStatus) {
//    		return null;
//    	}
    	
    	/*
    	 * 情况三:超时时间应该一次加,不应该分2行代码,
    	 * 
    	 */
    	boolean lockStatus = stringRedisTemplate.opsForValue().setIfAbsent(lock.intern(), driverId+"", 30L, TimeUnit.SECONDS);
    	// 开个子线程,原来时间N,每个n/3,去续上n
    	
    	if(!lockStatus) {
    		return null;
    	}
    	
    	try {
			System.out.println("司机:"+driverId+" 执行抢单逻辑");
			
            boolean b = orderService.grab(orderId, driverId);
            if(b) {
            	System.out.println("司机:"+driverId+" 抢单成功");
            }else {
            	System.out.println("司机:"+driverId+" 抢单失败");
            }
            
        } finally {
        	/**
        	 * 这种释放锁有,可能释放了别人的锁。
        	 */
//        	stringRedisTemplate.delete(lock.intern());
        	
        	/**
        	 * 下面代码避免释放别人的锁
        	 */
        	if((driverId+"").equals(stringRedisTemplate.opsForValue().get(lock.intern()))) {
				stringRedisTemplate.delete(lock.intern());
			}
        }
        return null;
    }
}

设置锁的有效时间,即redis设置key的有效期时间应该》业务执行的时间,简单来说就是程序执行的时间在锁的有效期内

redis 如果直接释放锁,即删除对应的key,这样可能会释放到别人锁,这里有遵循一个原则,谁加的锁谁释放

这里key值肯定是相同的,代表所争抢的资源,这里就是指订单的id

释放锁的方式有两种:争抢的key 相同

1.在value上做文章,可以取出key对应的value,确认是否是自己加的锁,这样就能正确的释放掉锁

2.写一个守护进程,当锁的资源(执行的业务逻辑)执行到有效期的1/3时,如果业务还没有执行完,则将锁续期(将key的有效期延长)至设置的有效期

续期的代码:

package com.online.taxi.order.service;

public interface RenewGrabLockService {

    /**
     * 续约
     * @param key
     * @param value
     * @param time
     */
    public void renewLock(String key , String value , int time);
}
package com.online.taxi.order.service.impl;

import com.online.taxi.order.service.RenewGrabLockService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;


@Service
public class RenewGrabLockServiceImpl implements RenewGrabLockService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    @Async
    public void renewLock(String key, String value, int time) {
        String v = redisTemplate.opsForValue().get(key);
        if (v.equals(value)){
            int sleepTime = time / 3;
            try {
                Thread.sleep(sleepTime * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            redisTemplate.expire(key,time,TimeUnit.SECONDS);
            renewLock(key,value,time);
        }
    }
}

 

如果直接删除掉key,为什么可能会释放掉别人的锁呢?

当运维重启时或者设置的锁的时间《执行业务逻辑的时间,这时业务还没执行完,锁就释放掉了,多个进程进来,就会释放掉别人的锁。

在service层使用@Async这个注解,需要在启动类上增加@EnableAsync 注解

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值