常用的锁有单体应用的简单锁synchronize,但是遇到分布式部署的项目时就会在大并发下出现安全问题,数据出现脏数据,此时可以考虑使用redis分布式锁,或者zookeeper锁
在实际开发中集群部署会出现的各种情况都会造成数据不安全,比如秒杀的库存等,或者服务器宕机或者重启,或者节点挂掉,这里使用redis分布式锁来实现锁机制

第一步:导入redission依赖
<dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.12.4</version>
</dependency>
第二步:封装CacheService常用方法类
package com.bigdata.hive.utlis;

import org.redisson.api.RAtomicLong;
import org.redisson.api.RBucket;
import org.redisson.api.RCountDownLatch;
import org.redisson.api.RDeque;
import org.redisson.api.RList;
import org.redisson.api.RLock;
import org.redisson.api.RMap;
import org.redisson.api.RQueue;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RSet;
import org.redisson.api.RSortedSet;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class CacheService {
    private final RedissonClient redissonClient;

    @Autowired
    public CacheService(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    public long deleteByPattern(String pattern) {
        return this.redissonClient.getKeys().deleteByPattern(pattern);
    }

    public boolean exists(String key) {
        return this.redissonClient.getKeys().countExists(new String[]{key}) == 1L;
    }

    public <T> RBucket<T> getRBucket(String key) {
        return this.redissonClient.getBucket(key);
    }

    public RLock getLock(String key) {
        return this.redissonClient.getLock(key);
    }

    public RReadWriteLock getRWLock(String key) {
        return this.redissonClient.getReadWriteLock(key);
    }

    public <K, V> RMap<K, V> getRMap(String key) {
        return this.redissonClient.getMap(key);
    }

    public <V> RSortedSet<V> getRSortedSet(String key) {
        return this.redissonClient.getSortedSet(key);
    }

    public <V> RSet<V> getRSet(String key) {
        return this.redissonClient.getSet(key);
    }

    public <V> RList<V> getRList(String key) {
        return this.redissonClient.getList(key);
    }

    public <V> RQueue<V> getRQueue(String key) {
        return this.redissonClient.getQueue(key);
    }

    public <V> RDeque<V> getRDeque(String key) {
        return this.redissonClient.getDeque(key);
    }

    public RAtomicLong getRAtomicLong(String key) {
        return this.redissonClient.getAtomicLong(key);
    }

    public RCountDownLatch getRCountDownLatch(String key) {
        return this.redissonClient.getCountDownLatch(key);
    }

    public RTopic getRTopic(String key) {
        return this.redissonClient.getTopic(key);
    }
}

第三步:注入CacheService使用redis分布式锁
 	@Autowired
    private CacheService cacheService;

实例:

package com.bigdata.hive.controller;

import com.bigdata.hive.utlis.CacheService;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 *@author liuxingying
 *@description
 *@date 2020/9/9
 */
public class RedisLockDemo {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private CacheService cacheService;

    @Autowired
    private Redisson redisson;

    /** @description 常规分布式redis加锁
     * @params [shopCode]
     * @return java.lang.String
     * @author lxy
     * @date 2020/9/9 17:08
     **/
    public String redisLock(String shopCode){
        //防止高并发下一个线程的锁被另一个线程给释放了,就造成锁一直加了就被释放导致锁失效
        String clientId = UUID.randomUUID().toString();

        try {
            //给redis加锁  setIfAbsent指shopCode存在的话就设置不了会返回false 不存在就设置成功返回true 从而实现上锁的效果
            //防止服务器宕机但是又给shopCode+"lock"设置成功了但释放不了,加一个过期时间10秒
            Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(shopCode+"lock", clientId,10, TimeUnit.SECONDS);
            if (!flag){
                //说明同时访问了,加了锁
                return "手速过快,请重新秒杀";
            }

            //加了锁就开始实现秒杀的业务 取出商品的数量
            Integer num = Integer.valueOf(stringRedisTemplate.opsForValue().get(shopCode));
            if (num>0){
                int stockNum = num -1;
                stringRedisTemplate.opsForValue().set(shopCode,stockNum+"");
                System.out.println("扣减成功,剩余库存:"+stockNum+"");
            }else {
                System.out.println("扣减失败,库存不足");
            }

        }finally {
            //判断线程的加锁和释放锁是同一线程防止被其他并发下的释放 导致的锁失效
            if (clientId.equals(stringRedisTemplate.opsForValue().get(shopCode+"lock"))){
                //最后要删除掉shopCode,相当于释放锁 不然除了第一单之外都进不去
                stringRedisTemplate.delete(shopCode);
            }
        }
        return "end";
    }

    /** @description redission分布式加锁
     * @params [shopCode]
     * @return java.lang.String
     * @author lxy
     * @date 2020/9/9 17:08
     **/
    public String redission(String shopCode){
        //redisson加锁 底层和上面的一样
        RLock redissonLock = redisson.getLock(shopCode + "lock");
        try {
            //锁30秒
            redissonLock.lock(30,TimeUnit.SECONDS);
            Boolean isLockSuccess = redissonLock.tryLock();
            if (!isLockSuccess){
                //说明同时访问了,加了锁
                return "手速过快,请重新秒杀";
            }

            //加了锁就开始实现秒杀的业务 取出商品的数量
            Integer num = Integer.valueOf(stringRedisTemplate.opsForValue().get(shopCode));
            if (num>0){
                int stockNum = num -1;
                stringRedisTemplate.opsForValue().set(shopCode,stockNum+"");
                System.out.println("扣减成功,剩余库存:"+stockNum+"");
            }else {
                System.out.println("扣减失败,库存不足");
            }
        }finally {
            //解锁
            redissonLock.unlock();
        }
        return "end";
    }

    /** @description redission分布式加锁 封装CacheService方法类
     * @params [shopCode]
     * @return java.lang.String
     * @author lxy
     * @date 2020/9/9 17:08
     **/
    public String cacheServiceDemo(String shopCode){
        RLock rLock =null;
        try {
            boolean isLockSuccess = false;
            try {
                rLock = cacheService.getLock(shopCode + "lock");
                isLockSuccess = rLock.tryLock();
            } catch (Exception e) {
                e.printStackTrace();
            }

            if (!isLockSuccess){
                //说明同时访问了,加了锁
                return "手速过快,请重新秒杀";
            }

            //加了锁就开始实现秒杀的业务 取出商品的数量
            Integer num = Integer.valueOf(stringRedisTemplate.opsForValue().get(shopCode));
            if (num>0){
                int stockNum = num -1;
                stringRedisTemplate.opsForValue().set(shopCode,stockNum+"");
                System.out.println("扣减成功,剩余库存:"+stockNum+"");
            }else {
                System.out.println("扣减失败,库存不足");
            }
        }finally {
            //判断线程的加锁和释放锁是同一线程防止被其他并发下的释放 导致的锁失效
            if (null != rLock && rLock.isLocked() && rLock.isHeldByCurrentThread()) {
                //解锁
                rLock.unlock();
            }
        }
        return "end";
    }
}

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐