利用Redis做多线程的锁(RedisLock)

1.前言

由于水平有限,总觉得哪里怪怪的。还请高手批评指正。

2.核心思想

当redis中,有这个key的时候,就认为已经有了锁;业务处理完后,清除redis中key,即认为清除了释放锁。

3.主要应用场景

当两个客户端同时操作一个资源时,客户端1需要审批该资源;客户端2需要撤回该资源。

4.关键源码所示

a)RedisLock.java

package com.wayne.demo.lock;

import com.wayne.demo.dao.RedisDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Component
public class RedisLock implements Lock {

    @Autowired
    private RedisDao redisDao;

    //标识当前key有没有被上锁
    private static Map<String,Boolean> lockedMap = new HashMap<>();

    @Override
    public boolean tryLock(String key) {
        synchronized (key){
            Object o = redisDao.get(0, key);
            if(null != o){
                return false;
            }else{
                return true;
            }
        }
    }

    @Override
    public void lock(String key) {
        synchronized (key){
            redisDao.save(0,key,key);
            lockedMap.put(key,true);
        }
    }

    @Override
    public void unlock(String key) {
        synchronized (key){
            redisDao.removeData(0,key);
            lockedMap.put(key,false);
            lockedMap.remove(key);
        }
    }

    @Override
    public boolean isLocked(String key) {
        return lockedMap.get(key) == null?false:lockedMap.get(key);
    }
}

b)ThreadController.java

package com.wayne.demo.comtrollor;

import com.wayne.demo.lock.RedisLock;
import com.wayne.demo.params.BaseParams;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.concurrent.TimeUnit;

@Slf4j
@RestController
@RequestMapping("/threadController")
public class ThreadController {

    private static Logger logger = LoggerFactory.getLogger(ThreadController.class);

    @Autowired
    private RedisLock redisLock;

    @PostMapping("/submitApproval")
    void submitApproval(@RequestBody BaseParams params){
        logger.info("某个线程提交了审核");
        logger.info("我开始处理审核");


        if(!redisLock.isLocked(params.getKey())){
            logger.info("其他资源没有占用");
            logger.info("我开始处理审核");
            redisLock.lock(params.getKey());

            try {
                TimeUnit.MINUTES.sleep(10);
            }catch (InterruptedException e){
               e.printStackTrace();
            } finally {
                redisLock.unlock(params.getKey());
            }
        }else{
            logger.info("其他资源正在处理");
            logger.info("我无法处理审核");
        }
    }

    @PostMapping("/cancelAppral")
    void cancelAppral(@RequestBody BaseParams params){
        logger.info("某个线程撤销了审核");

        if(!redisLock.isLocked(params.getKey())){
            logger.info("其他资源没有占用");
            logger.info("我开始处理撤销了审核");
            redisLock.lock(params.getKey());

            try {
                TimeUnit.MINUTES.sleep(5);
            }catch (InterruptedException e){
                e.printStackTrace();
            } finally {
                redisLock.unlock(params.getKey());
            }

        }else{
            logger.info("其他资源正在处理");
            logger.info("我无法处理撤销了审核");
        }
    }
}

5.测试结果

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以的,利用 Redis 实现分布式的方法有很多,其中比较常用的是基于 Redis 的 SETNX 命令实现的。具体实现方法可以参考以下步骤: 1. 在 Redis 中创建一个键,作为的标识符。 2. 使用 SETNX 命令尝试将该键的值设置为一个唯一的标识符,如果设置成功,则表示获取成功。 3. 如果 SETNX 命令返回 ,则表示已经被其他客户端获取,此时可以等待一段时间后重试获取。 4. 在获取成功后,可以执行需要加的代码,执行完毕后使用 DEL 命令删除该键,释放。 需要注意的是,在使用 Redis 实现分布式时,需要考虑的超时时间和的可重入性等问题,以保证的正确性和可靠性。 ### 回答2: 分布式是一种用于协调分布式系统中并发访问共享资源的机制。利用Redis来实现分布式是一种常见的方式,下面是使用Java编写一个简单的分布式的示例: 首先,我们需要引入Redis客户端的依赖,比如 jedis,确保项目中已经添加了相应的依赖。 然后,在Java代码中,我们可以使用Redis的setnx命令来尝试获取。该命令会在指定的key不存在时,将key的值设为指定的字符串,如果key已经存在,则不任何操作。 代码示例: ```java import redis.clients.jedis.Jedis; public class DistributedLock { private static final String LOCK_KEY = "distributed_lock"; private static final int EXPIRE_TIME = 30000; private static final int WAIT_TIME = 1000; private static final int SLEEP_TIME = 100; private Jedis jedis; public DistributedLock(Jedis jedis) { this.jedis = jedis; } public boolean acquireLock() { long start = System.currentTimeMillis(); try { while (System.currentTimeMillis() - start < WAIT_TIME) { if (jedis.setnx(LOCK_KEY, String.valueOf(System.currentTimeMillis() + EXPIRE_TIME)) == 1) { // 成功获取 return true; } Thread.sleep(SLEEP_TIME); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return false; } public void releaseLock() { jedis.del(LOCK_KEY); } } ``` 在上述代码中,acquireLock方法用于尝试获取分布式,首先会不断地尝试执行setnx命令来设置。如果成功设置了,则返回true,表示获取成功。如果在指定的等待时间内没有获取到,则返回false。 releaseLock方法用于释放,通过del命令删除指定的key来实现。 需要注意的是,在使用完之后一定要及时调用releaseLock方法来释放,避免出现死的情况。 这只是一个简单的分布式示例,实际在生产环境中的分布式需要考虑更多的情况,比如的可重入性、的失效机制等。同时,由于Redis是内存数据库,分布式只能保证粗粒度的定,不能保证细粒度的互斥,因此在实际应用中需要根据业务场景进行合理的设计和使用。 ### 回答3: 分布式是一种并发控制技术,它允许只有一个进程或线程能够访问共享资源,从而避免并发操作引起的数据不一致问题。Redis是一种高性能的键值存储数据库,它支持多种数据结构和一些特殊的操作,例如分布式。 在Java中使用Redis实现分布式的步骤如下: 1. 引入Redis的Java客户端依赖,例如Jedis或Lettuce。 2. 使用Redis连接池或连接工厂创建Redis连接。 3. 在需要使用分布式的代码块中,首先尝试获取。可以使用Redis的`SETNX`命令来设置一个带有过期时间的,通过给设置一个唯一的标识来确保只有一个线程或进程能够获取到。 4. 如果获取成功,则执行需要保护的代码逻辑。 5. 在代码逻辑执行完成后,释放。可以使用Redis的`DEL`命令来删除之前设置的。 以下是一个简单的使用Redis和Java实现分布式的示例代码: ```java import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; public class DistributedLockExample { private static final String LOCK_KEY = "my_lock"; private static final int LOCK_EXPIRE_TIME = 60; // 过期时间(秒) private JedisPool jedisPool; public DistributedLockExample() { JedisPoolConfig poolConfig = new JedisPoolConfig(); poolConfig.setMaxTotal(10); poolConfig.setMaxIdle(5); poolConfig.setMinIdle(1); jedisPool = new JedisPool(poolConfig, "localhost", 6379); } public void doSomethingWithLock() { Jedis jedis = jedisPool.getResource(); try { // 尝试获取 long lockInMilliseconds = System.currentTimeMillis() + (LOCK_EXPIRE_TIME * 1000); boolean locked = jedis.setnx(LOCK_KEY, String.valueOf(lockInMilliseconds)) == 1; if (locked) { jedis.expire(LOCK_KEY, LOCK_EXPIRE_TIME); // 执行需要保护的代码逻辑 System.out.println("Do something..."); } else { System.out.println("Failed to acquire lock"); } } finally { // 释放 jedis.del(LOCK_KEY); jedis.close(); } } public static void main(String[] args) { DistributedLockExample example = new DistributedLockExample(); example.doSomethingWithLock(); } } ``` 在上面的示例代码中,我们创建了一个`DistributedLockExample`类,它通过连接池获取Redis连接,并在`doSomethingWithLock`方法中实现了对的获取与释放。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值