Skip to content
/ lock Public

The distributed lock tool for java.(java 实现开箱即用基于 redis 的分布式锁,支持可重入锁获取。内置整合 spring、springboot。)

License

Notifications You must be signed in to change notification settings

houbb/lock

Repository files navigation

项目简介

lock 为 java 设计的渐进式分布式锁,开箱即用,纵享丝滑。

Maven Central Build Status Coverage Status

开源地址:https://github.com/houbb/lock

目的

  • 开箱即用,支持注解式和过程式调用

  • 支持可重入锁获取

  • 基于 mysql 的分布式锁

  • 基于 redis 的分布式锁

  • 内置支持多种 redis 的整合方式

  • 渐进式设计,可独立于 spring 使用

  • 整合 spring

  • 整合 spring-boot

变更日志

变更日志

快速开始

需要

jdk1.7+

maven 3.x+

基于 MAP 的分布式锁

maven 引入

<dependency>
    <groupId>com.github.houbb</groupId>
    <artifactId>lock-core</artifactId>
    <version>1.7.2</version>
</dependency>

入门例子

基于本地 MAP 的入门测试案例。

ILock lock = LockBs.newInstance();
String key = "ddd";
try {
    // 加锁
    boolean lockFlag = lock.tryLock(key);
    Assert.assertTrue(lockFlag);
} catch (Exception e) {
    throw new RuntimeException(e);
} finally {
    // 释放锁
    lock.unlock(key);
}

接口说明

接口方法文档

方法签名 参数说明 返回值说明 版本标记
boolean tryLock(final ILockContext context) context: 锁操作上下文对象 加锁成功返回 true,失败 false since 1.5.0
boolean tryLock(String key, TimeUnit timeUnit, long lockTime, long waitLockTime, boolean reentrant) key: 锁标识
timeUnit: 时间单位
lockTime: 锁持有时长
waitLockTime: 最大等待时长
reentrant: 是否允许重入
加锁成功返回 true,失败 false since 1.5.0
boolean tryLock(String key, TimeUnit timeUnit, long lockTime, long waitLockTime) key: 锁标识
timeUnit: 时间单位
lockTime: 锁持有时长
waitLockTime: 最大等待时长
(默认 reentrant=true)
加锁成功返回 true,失败 false since 0.0.1
boolean tryLock(String key, TimeUnit timeUnit, long lockTime) key: 锁标识
timeUnit: 时间单位
lockTime: 锁持有时长
(默认 waitLockTime=0 仅尝试一次)
加锁成功返回 true,失败 false since 0.0.1
boolean tryLock(String key, long lockTime) key: 锁标识
lockTime: 锁持有时长(秒)
(默认 timeUnit=SECONDS)
加锁成功返回 true,失败 false since 0.0.1
boolean tryLock(String key) key: 锁标识
(默认 lockTime=10秒)
加锁成功返回 true,失败 false since 0.0.1
boolean unlock(String key) key: 锁标识
注意: 释放锁不重试,所有 key 有过期时间
释放成功返回 true,失败 false since 0.0.1

tryLock 方法说明

提供了较多方法,只是为了使用更加便捷。

boolean tryLock(final ILockContext context); 中的入参,和参数一一对应,默认值也相同。

context 的引入,为了避免后续的配置项较多,方法会膨胀的问题。

ILockContext lockContext = LockContext.newInstance()
        .key(key)
        .lockTime(LockConst.DEFAULT_LOCK_TIME)
        .waitLockTime(LockConst.DEFAULT_WAIT_LOCK_TIME)
        .reentrant(LockConst.DEFAULT_REENTRANT)
        .timeUnit(LockConst.DEFAULT_TIME_UNIT);

boolean lockFlag = lock.tryLock(lockContext);

锁的一些特性

锁的可重入性

概念:可重入性(reentrancy)是指一个线程在拥有一个资源(通常是一个锁)的情况下,再次获取该资源时不会造成死锁。可重入性在多线程编程中非常重要,因为它可以避免因线程之间的相互依赖而导致的死锁。

可重入的例子

我们支持一个线程可以多次获取一个锁,默认是可重入的。

@Test
public void reTest() {
    ILock lock = LockBs.newInstance();
    String key = "ddd";
    try {
        // 加锁
        boolean lockFlag = lock.tryLock(key);
        //1. 首次获取锁成功
        Assert.assertTrue(lockFlag);
        //2. 重新获取锁成功
        boolean reLockFlag = lock.tryLock(key);
        Assert.assertTrue(reLockFlag);
    } catch (Exception e) {
        throw new RuntimeException(e);
    } finally {
        // 释放锁
        lock.unlock(key);
    }
}

不可重入的例子

当然,我们也支持不可重入的形式,指定对应的配置即可。

public void noReTest() {
    ILock lock = LockBs.newInstance();
    String key = "ddd";
    try {
        ILockContext lockContext = LockContext.newInstance()
                .key(key)
                .waitLockTime(5)
                .reentrant(false);  // 指定不可重入

        boolean lockFlag = lock.tryLock(lockContext);
        //1. 首次获取锁成功
        Assert.assertTrue(lockFlag);
        //2. 不是重入,第二次获取失败
        boolean reLockFlag = lock.tryLock(lockContext);
        Assert.assertFalse(reLockFlag);
    } catch (Exception e) {
        throw new RuntimeException(e);
    } finally {
        // 释放锁
        lock.unlock(key);
    }
}

不可重入锁,第二次获取就会失败。

配置化

为了便于拓展,LockBs 的配置支持自定义:

LockBs.newInstance()
        .id(Ids.uuid32())   //id 生成策略
        .cache(JedisRedisServiceFactory.pooled("127.0.0.1", 6379)) //缓存策略
        .lockSupport(new RedisLockSupport())    // 锁实现策略
        .lockKeyFormat(new LockKeyFormat())     // 针对 key 的格式化处理策略
        .lockReleaseFailHandler(new LockReleaseFailHandler())   //释放锁失败处理
        ;

可配置项如下:

属性配置说明

属性名 类型 默认值 说明 版本标记
id Id Ids.uuid32() 唯一标识生成策略 since 0.0.4
cache ICommonCacheService new CommonCacheServiceMap() 缓存实现策略 since 0.0.4
lockSupport ILockSupport new CommonCacheLockSupport() 锁底层支持策略 since 1.0.0
lockKeyFormat ILockKeyFormat new LockKeyFormat() 锁 Key 格式化处理器 since 1.2.0
lockReleaseFailHandler ILockReleaseFailHandler new LockReleaseFailHandler() 锁释放失败处理策略 since 1.2.0
tryLockIntervalMills int LockConst.DEFAULT_TRY_LOCK_INTERVAL_MILLS 尝试加锁的轮询间隔(毫秒) since 1.6.0

整合 spring

maven 引入

<dependency>
    <groupId>com.github.houbb</groupId>
    <artifactId>lock-spring</artifactId>
    <version>1.7.2</version>
</dependency>

指定 bean 使用

启用分布式锁

@EnableLock 启用分布式锁。

@EnableRedisConfig 启用 redis 的默认配置。

@Configurable
@ComponentScan(basePackages = "com.github.houbb.lock.test.service")
@EnableLock
@EnableRedisConfig
public class SpringConfig {
}

EnableLock 注解说明,和引导类对应:

@EnableLock 注解属性说明

属性名 描述 默认值
id() 唯一标识生成策略 "lockId"
cache() 缓存实现策略的 Bean 名称 "springRedisService"
lockKeyFormat() 加锁 Key 格式化策略 "lockKeyFormat"
lockKeyNamespace() 锁 Key 的默认命名空间 LockConst.DEFAULT_LOCK_KEY_NAMESPACE
lockReleaseFailHandler() 锁释放失败处理类 "lockReleaseFailHandler"

其中 springRedisService 使用的是 redis-config 中的实现。

对应注解 @EnableRedisConfig,redis 的配置信息如下:

配置 说明 默认值
redis.address redis 地址 127.0.0.1
redis.port redis 端口 6379
redis.password redis 密码

当然,你可以使用自己想用的其他缓存实现,只需要实现对应的接口标准即可。

使用 LockBs

我们可以直接 LockBs 的引导类,这种适合一些更加灵活的场景。

@ContextConfiguration(classes = SpringConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringServiceRawTest {

    @Autowired
    private UserService userService;

    @Autowired
    private LockBs lockBs;

    @Test
    public void queryLogTest() {
        final String key = "name";
        try {
            boolean lockFlag = lockBs.tryLock(key);
            final String value = userService.rawUserName(1L);
        } catch (Exception exception) {
            throw new RuntimeException(exception);
        } finally {
            lockBs.unlock(key);
        }
    }

}

aop 注解使用

指定方法注解

当然,我们可以在方法上直接指定注解 @Lock,使用更加方便。

直接使用,AOP 切面生效即可。

@Service
public class UserService {

    @Lock
    public String queryUserName(Long userId) {
    }

    @Lock(value = "#user.name")
    public void queryUserName2(User user) {
    }
}

@Lock 属性说明,value 用于指定 key,支持 SPEL 表达式。

如果 aop 中拦截获取锁失败,默认会抛出异常。

其他属性,和引导类的方法参数一一对应。

@Lock 注解属性说明

属性名 类型 默认值 说明
value() String "" 锁的 Key 策略(支持 SpEL 表达式)
例:@Lock("order:#{#orderId}")
timeUnit() TimeUnit TimeUnit.SECONDS 时间单位(秒/毫秒等)
waitLockTime() long LockConst.DEFAULT_WAIT_LOCK_TIME 等待锁的最长时间
通常为 0(仅尝试一次)或正数(持续重试)
lockTime() long LockConst.DEFAULT_LOCK_TIME 锁持有时间
通常为 10-30 秒(防止死锁)
reentrant() boolean LockConst.DEFAULT_REENTRANT 是否允许重入
通常为 true(同一线程可重复获取)

spring boot 整合

maven 引入

<dependency>
    <groupId>com.github.houbb</groupId>
    <artifactId>lock-springboot-starter</artifactId>
    <version>1.7.2</version>
</dependency>

使用

所有的配置会自动生效。

使用方式同 spring。

后期 Road-MAP

  • 支持锁的可重入

持有锁的线程可以多次获取锁

  • 分布式锁注解支持

  • spring 的锁支持拓展性扩展,而不是局限于 redis-lock

  • watch-dog,添加锁的自动续租?

拓展阅读

Redis 分布式锁

java 从零实现 redis 分布式锁

开源矩阵

下面是一些缓存系列的开源矩阵规划。

名称 介绍 状态
resubmit 防止重复提交核心库 已开源
rate-limit 限流核心库 已开源
cache 手写渐进式 redis 已开源
lock 开箱即用的分布式锁 已开源
common-cache 通用缓存标准定义 已开源
redis-config 兼容各种常见的 redis 配置模式 已开源
quota-server 限额限次核心服务 待开始
quota-admin 限额限次控台 待开始
flow-control-server 流控核心服务 待开始
flow-control-admin 流控控台 待开始

About

The distributed lock tool for java.(java 实现开箱即用基于 redis 的分布式锁,支持可重入锁获取。内置整合 spring、springboot。)

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published