lock 为 java 设计的渐进式分布式锁,开箱即用,纵享丝滑。
开源地址:https://github.com/houbb/lock
- 
开箱即用,支持注解式和过程式调用 
- 
支持可重入锁获取 
- 
基于 mysql 的分布式锁 
- 
基于 redis 的分布式锁 
- 
内置支持多种 redis 的整合方式 
- 
渐进式设计,可独立于 spring 使用 
- 
整合 spring 
- 
整合 spring-boot 
jdk1.7+
maven 3.x+
<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 | 
提供了较多方法,只是为了使用更加便捷。
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 | 
<dependency>
    <groupId>com.github.houbb</groupId>
    <artifactId>lock-spring</artifactId>
    <version>1.7.2</version>
</dependency>@EnableLock 启用分布式锁。
@EnableRedisConfig 启用 redis 的默认配置。
@Configurable
@ComponentScan(basePackages = "com.github.houbb.lock.test.service")
@EnableLock
@EnableRedisConfig
public class SpringConfig {
}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 的引导类,这种适合一些更加灵活的场景。
@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);
        }
    }
}当然,我们可以在方法上直接指定注解 @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 中拦截获取锁失败,默认会抛出异常。
其他属性,和引导类的方法参数一一对应。
| 属性名 | 类型 | 默认值 | 说明 | 
|---|---|---|---|
| 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(同一线程可重复获取) | 
<dependency>
    <groupId>com.github.houbb</groupId>
    <artifactId>lock-springboot-starter</artifactId>
    <version>1.7.2</version>
</dependency>所有的配置会自动生效。
使用方式同 spring。
- 支持锁的可重入
持有锁的线程可以多次获取锁
- 
分布式锁注解支持 
- 
spring 的锁支持拓展性扩展,而不是局限于 redis-lock 
- 
watch-dog,添加锁的自动续租? 
下面是一些缓存系列的开源矩阵规划。
| 名称 | 介绍 | 状态 | 
|---|---|---|
| resubmit | 防止重复提交核心库 | 已开源 | 
| rate-limit | 限流核心库 | 已开源 | 
| cache | 手写渐进式 redis | 已开源 | 
| lock | 开箱即用的分布式锁 | 已开源 | 
| common-cache | 通用缓存标准定义 | 已开源 | 
| redis-config | 兼容各种常见的 redis 配置模式 | 已开源 | 
| quota-server | 限额限次核心服务 | 待开始 | 
| quota-admin | 限额限次控台 | 待开始 | 
| flow-control-server | 流控核心服务 | 待开始 | 
| flow-control-admin | 流控控台 | 待开始 |