maven依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.14</version>
</dependency>

 

帮助类 采用redis的setNX实现

  • SETNX(SET If Not Exists):当且仅当 Key 不存在时,则可以设置,否则不做任何动作。

 

package com.whq.test;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class DistributedLockHelper {

    private final static long LOCK_EXPIRE = 30 * 1000L;//单个业务持有锁的时间30s,防止死锁
    private final static long LOCK_TRY_INTERVAL = 30L;//默认30ms尝试一次
    private final static long LOCK_TRY_TIMEOUT = 20 * 1000L;//默认尝试20s

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 尝试获取全局锁
     *
     * @param key 锁的名称
     * @return true 获取成功,false获取失败
     */
    public boolean tryLock(String key) {
        return getLock(key, LOCK_TRY_TIMEOUT, LOCK_TRY_INTERVAL, LOCK_EXPIRE);
    }

    /**
     * 尝试获取全局锁
     *
     * @param key    锁的名称
     * @param timeout 获取超时时间 单位ms
     * @return true 获取成功,false获取失败
     */
    public boolean tryLock(String key, long timeout) {
        return getLock(key, timeout, LOCK_TRY_INTERVAL, LOCK_EXPIRE);
    }

    /**
     * 尝试获取全局锁
     *
     * @param key        锁的名称
     * @param timeout     获取锁的超时时间
     * @param tryInterval 多少毫秒尝试获取一次
     * @return true 获取成功,false获取失败
     */
    public boolean tryLock(String key, long timeout, long tryInterval) {
        return getLock(key, timeout, tryInterval, LOCK_EXPIRE);
    }

    /**
     * 尝试获取全局锁
     *
     * @param key           锁的名称
     * @param timeout        获取锁的超时时间
     * @param tryInterval    多少毫秒尝试获取一次
     * @param lockExpireTime 锁的过期
     * @return true 获取成功,false获取失败
     */
    public boolean tryLock(String key, long timeout, long tryInterval, long lockExpireTime) {
        return getLock(key, timeout, tryInterval, lockExpireTime);
    }


    /**
     * 操作redis获取全局锁
     *
     * @param key           锁的名称
     * @param timeout        获取的超时时间
     * @param tryInterval    多少ms尝试一次
     * @param lockExpireTime 获取成功后锁的过期时间
     * @return true 获取成功,false获取失败
     */
    public boolean getLock(String key, long timeout, long tryInterval, long lockExpireTime) {
        try {
            if (StringUtils.isEmpty(key)) {
                return false;
            }
            long startTime = System.currentTimeMillis();
            while(true){
                if (!stringRedisTemplate.hasKey(key)) {
                    ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
                    if(ops.setIfAbsent(key, "", lockExpireTime, TimeUnit.MILLISECONDS)) {
                        return true;
                    }
                } else {//存在锁
                    log.debug("lock is exist!!!");
                }
                if (System.currentTimeMillis() - startTime > timeout) {//尝试超过了设定值之后直接跳出循环
                    return false;
                }
                Thread.sleep(tryInterval);
            }
        } catch (InterruptedException e) {
            log.error(e.getMessage());
            return false;
        }
    }

    /**
     * 释放锁
     */
    public void releaseLock(String key) {
        if (!StringUtils.isEmpty(key)) {
            stringRedisTemplate.delete(key);
        }
    }

}

调用测试

 

@RestController
@SpringBootApplication
@Slf4j
public class TestApplication {

    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);

    }
    @Autowired
    private DistributedLockHelper distributedLockHandler;

    @RequestMapping("lock")
    public String lock(){
        String key="testlock";
        log.info("准备获取锁");
        if(distributedLockHandler.tryLock(key)){
            try {
                //为了演示锁的效果,这里睡眠5000毫秒
                log.info("已经获取到锁");
                Thread.sleep(5000);
            }catch (Exception e){
                e.printStackTrace();
            }
            distributedLockHandler.releaseLock(key);
            log.info("锁已释放");
        }
        return "hello world!";
    }
}

配置application.yml


server:
  port: 18081

spring:
  redis:
    host: 10.0.197.189
    port: 6379

 

注意:测试时要采用两个浏览器,或者一个用ip、一个用localhost访问,否则浏览器会同时只能进行一个请求。

测试输出日志

 

2019-07-23 14:42:52 1997495 [http-nio-18081-exec-1] INFO  com.whq.test.TestApplication - 准备获取锁 
2019-07-23 14:42:52 1997507 [http-nio-18081-exec-1] INFO  com.whq.test.TestApplication - 已经获取到锁 
2019-07-23 14:42:53 1998452 [http-nio-18081-exec-3] INFO  com.whq.test.TestApplication - 准备获取锁 
2019-07-23 14:42:57 2002510 [http-nio-18081-exec-1] INFO  com.whq.test.TestApplication - 锁已释放 
2019-07-23 14:42:57 2002530 [http-nio-18081-exec-3] INFO  com.whq.test.TestApplication - 已经获取到锁 
2019-07-23 14:43:02 2007535 [http-nio-18081-exec-3] INFO  com.whq.test.TestApplication - 锁已释放 

 

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐