Compare commits
11 Commits
release_0.
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ef657d0ba | ||
|
|
0f9ce0441e | ||
|
|
bc1fd8fc71 | ||
|
|
4ce823e161 | ||
|
|
62d2a60edc | ||
|
|
0bae77fc8a | ||
|
|
c45f6b53a0 | ||
|
|
27a931ae77 | ||
|
|
8bf1df19c2 | ||
|
|
c0e3cc4bf2 | ||
|
|
402653393d |
27
CHANGELOG.md
27
CHANGELOG.md
@@ -32,3 +32,30 @@
|
||||
| 序号 | 变更类型 | 说明 | 时间 | 备注 |
|
||||
|:---|:---|:---|:---|:--|
|
||||
| 1 | A | 简单锁的实现,优化 redisLock 实现策略 | 2022-04-17 14:45:40 | |
|
||||
|
||||
|
||||
# release_1.0.0
|
||||
|
||||
| 序号 | 变更类型 | 说明 | 时间 | 备注 |
|
||||
|:---|:---|:---|:---|:--|
|
||||
| 1 | A | 基于 redis 实现的分布式锁策略 | 2022-12-7 14:45:40 | |
|
||||
|
||||
# release_1.1.0
|
||||
|
||||
| 序号 | 变更类型 | 说明 | 时间 | 备注 |
|
||||
|:---|:---|:---|:---|:--|
|
||||
| 1 | A | 整合 spring | 2022-12-7 14:45:40 | |
|
||||
|
||||
# release_1.2.0
|
||||
|
||||
| 序号 | 变更类型 | 说明 | 时间 | 备注 |
|
||||
|:---|:---|:---|:---|:--|
|
||||
| 1 | A | 添加 keyFormat | 2022-12-8 14:45:40 | |
|
||||
| 2 | A | 添加锁释放失败 | 2022-12-8 14:45:40 | |
|
||||
| 3 | O | 加锁处理优化,接口统一优化 | 2022-12-8 14:45:40 | |
|
||||
|
||||
# release_1.3.0
|
||||
|
||||
| 序号 | 变更类型 | 说明 | 时间 | 备注 |
|
||||
|:---|:---|:---|:---|:--|
|
||||
| 1 | A | 让 redis-config 主动启用,避免无法主动管理的情况 | 2022-12-8 14:45:40 | |
|
||||
|
||||
255
README.md
255
README.md
@@ -1,18 +1,26 @@
|
||||
# 项目简介
|
||||
|
||||
为 java 设计的锁。
|
||||
[lock](https://github.com/houbb/lock) 为 java 设计的分布式锁,开箱即用,纵享丝滑。
|
||||
|
||||
[](http://mvnrepository.com/artifact/com.github.houbb/lock)
|
||||
[](https://www.travis-ci.org/houbb/lock?branch=master)
|
||||
[](https://coveralls.io/github/houbb/lock?branch=master)
|
||||
|
||||
开源地址:[https://github.com/houbb/lock](https://github.com/houbb/lock)
|
||||
|
||||
## 目的
|
||||
|
||||
- 开箱即用,支持注解式和过程式调用
|
||||
|
||||
- 基于 redis 的分布式锁
|
||||
|
||||
- 基于 oracle 的分布式锁
|
||||
- 内置支持多种 redis 的整合方式
|
||||
|
||||
- 基于 mysql 的分布式锁
|
||||
- 渐进式设计,可独立于 spring 使用
|
||||
|
||||
- 整合 spring
|
||||
|
||||
- 整合 spring-boot
|
||||
|
||||
# 变更日志
|
||||
|
||||
@@ -32,7 +40,7 @@ maven 3.x+
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>lock-core</artifactId>
|
||||
<version>0.0.4</version>
|
||||
<version>1.3.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
@@ -41,38 +49,237 @@ maven 3.x+
|
||||
基于本地 redis 的测试案例。
|
||||
|
||||
```java
|
||||
Jedis jedis = new Jedis("127.0.0.1", 6379);
|
||||
IOperator operator = new JedisOperator(jedis);
|
||||
|
||||
// 获取锁
|
||||
ILock lock = LockBs.newInstance(operator).lock();
|
||||
|
||||
try {
|
||||
boolean lockResult = lock.tryLock();
|
||||
System.out.println(lockResult);
|
||||
// 业务处理
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
public void helloTest() {
|
||||
ILock lock = LockBs.newInstance();
|
||||
String key = "ddd";
|
||||
try {
|
||||
// 加锁
|
||||
lock.tryLock(key);
|
||||
System.out.println("业务处理");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
// 释放锁
|
||||
lock.unlock(key);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 配置化
|
||||
|
||||
为了便于拓展,LockBs 的配置支持自定义:
|
||||
|
||||
```java
|
||||
LockBs.newInstance()
|
||||
.id(Ids.uuid32()) //id 生成策略
|
||||
.cache(JedisRedisServiceFactory.pooled("127.0.0.1", 6379)) //缓存策略
|
||||
.lockSupport(new RedisLockSupport()) // 锁实现策略
|
||||
.lockKeyFormat(new LockKeyFormat()) // 针对 key 的格式化处理策略
|
||||
.lockReleaseFailHandler(new LockReleaseFailHandler()) //释放锁失败处理
|
||||
;
|
||||
```
|
||||
|
||||
# 整合 spring
|
||||
|
||||
## maven 引入
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>lock-spring</artifactId>
|
||||
<version>1.3.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## 指定 bean 使用
|
||||
|
||||
### 启用分布式锁
|
||||
|
||||
`@EnableLock` 启用分布式锁。
|
||||
|
||||
`@EnableRedisConfig` 启用 redis 的默认配置。
|
||||
|
||||
```xml
|
||||
@Configurable
|
||||
@ComponentScan(basePackages = "com.github.houbb.lock.test.service")
|
||||
@EnableLock
|
||||
@EnableRedisConfig
|
||||
public class SpringConfig {
|
||||
}
|
||||
```
|
||||
|
||||
`EnableLock` 注解说明,和引导类对应:
|
||||
|
||||
```java
|
||||
public @interface EnableLock {
|
||||
|
||||
/**
|
||||
* 唯一标识生成策略
|
||||
* @return 结果
|
||||
*/
|
||||
String id() default "lockId";
|
||||
|
||||
/**
|
||||
* 缓存实现策略 bean 名称
|
||||
*
|
||||
* 默认引入 redis-config 中的配置
|
||||
*
|
||||
* @return 实现
|
||||
*/
|
||||
String cache() default "springRedisService";
|
||||
|
||||
/**
|
||||
* 加锁 key 格式化策略
|
||||
* @return 策略
|
||||
*/
|
||||
String lockKeyFormat() default "lockKeyFormat";
|
||||
|
||||
/**
|
||||
* 锁释放失败处理类
|
||||
* @return 结果
|
||||
*/
|
||||
String lockReleaseFailHandler() default "lockReleaseFailHandler";
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
其中 `springRedisService` 使用的是 [redis-config](https://github.com/houbb/redis-config) 中的实现。
|
||||
|
||||
对应注解 `@EnableRedisConfig`,redis 的配置信息如下:
|
||||
|
||||
| 配置 | 说明 | 默认值
|
||||
|:---|:---|:----|
|
||||
| redis.address | redis 地址 | 127.0.0.1 |
|
||||
| redis.port | redis 端口 | 6379 |
|
||||
| redis.password | redis 密码 | |
|
||||
|
||||
### 使用 LockBs
|
||||
|
||||
我们可以直接 `LockBs` 的引导类,这种适合一些更加灵活的场景。
|
||||
|
||||
```java
|
||||
@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 {
|
||||
lockBs.tryLock(key);
|
||||
final String value = userService.rawUserName(1L);
|
||||
} catch (Exception exception) {
|
||||
throw new RuntimeException(exception);
|
||||
} finally {
|
||||
lockBs.unlock(key);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## aop 注解使用
|
||||
|
||||
### 指定方法注解
|
||||
|
||||
当然,我们可以在方法上直接指定注解 `@Lock`,使用更加方便。
|
||||
|
||||
直接使用,AOP 切面生效即可。
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class UserService {
|
||||
|
||||
@Lock
|
||||
public String queryUserName(Long userId) {
|
||||
}
|
||||
|
||||
@Lock(value = "#user.name")
|
||||
public void queryUserName2(User user) {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`@Lock` 属性说明,value 用于指定 key,支持 SPEL 表达式。
|
||||
|
||||
其他属性,和引导类的方法参数一一对应。
|
||||
|
||||
```java
|
||||
public @interface Lock {
|
||||
|
||||
/**
|
||||
* 缓存的 key 策略,支持 SpEL
|
||||
* @return 结果
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* 时间单位
|
||||
* @return 单位
|
||||
*/
|
||||
TimeUnit timeUnit() default TimeUnit.SECONDS;
|
||||
|
||||
/**
|
||||
* 等待锁时间
|
||||
* @return 等待锁时间
|
||||
*/
|
||||
long waitLockTime() default 10;
|
||||
|
||||
/**
|
||||
* 业务加锁时间
|
||||
* @return 加锁时间
|
||||
*/
|
||||
long lockTime() default 60;
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
# spring boot 整合
|
||||
|
||||
## maven 引入
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>lock-springboot-starter</artifactId>
|
||||
<version>1.3.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## 使用
|
||||
|
||||
同 spring
|
||||
|
||||
# 后期 Road-MAP
|
||||
|
||||
- [ ] 支持锁的可重入
|
||||
|
||||
持有锁的线程可以多次获取锁
|
||||
|
||||
- [ ] redis 实现的支持
|
||||
- [x] 分布式锁注解支持
|
||||
|
||||
cluster 支持
|
||||
# 拓展阅读
|
||||
|
||||
redis 支持
|
||||
[Redis 分布式锁](https://houbb.github.io/2018/09/08/redis-learn-42-distributed-lock-redis)
|
||||
|
||||
aliyun-redis 支持
|
||||
[java 从零实现 redis 分布式锁](https://houbb.github.io/2018/09/08/redis-learn-43-distributed-lock-redis-java-impl)
|
||||
|
||||
各种各样的声明方式的默认支持
|
||||
# 缓存相关工具
|
||||
|
||||
- [ ] 分布式锁注解支持
|
||||
[cache: 手写渐进式 redis](https://github.com/houbb/cache)
|
||||
|
||||
[common-cache: 通用缓存标准定义](https://github.com/houbb/common-cache)
|
||||
|
||||
[redis-config: 兼容各种常见的 redis 配置模式](https://github.com/houbb/redis-config)
|
||||
|
||||
[lock: 开箱即用的分布式锁](https://github.com/houbb/lock)
|
||||
|
||||
[resubmit: 防重复提交](https://github.com/houbb/resubmit)
|
||||
|
||||
[rate-limit: 限流](https://github.com/houbb/rate-limit/)
|
||||
|
||||
@@ -5,11 +5,21 @@
|
||||
<parent>
|
||||
<artifactId>lock</artifactId>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<version>0.0.4</version>
|
||||
<version>1.3.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>lock-api</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>common-cache-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>id-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -1,29 +1,47 @@
|
||||
package com.github.houbb.lock.api.core;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
/**
|
||||
* 锁定义
|
||||
* 分布式锁接口定义
|
||||
* @author binbin.hou
|
||||
* @since 0.0.1
|
||||
*/
|
||||
public interface ILock extends Lock {
|
||||
public interface ILock {
|
||||
|
||||
/**
|
||||
* 尝试加锁
|
||||
* @param time 时间
|
||||
* @param unit 当为
|
||||
* 尝试加锁,如果失败,会一直尝试。
|
||||
*
|
||||
* @param key key
|
||||
* @param timeUnit 时间单位
|
||||
* @param waitLockTime 等待锁时间
|
||||
* @param lockTime 加锁时间
|
||||
* @return 返回
|
||||
* @throws InterruptedException 异常
|
||||
* @since 0.0.1
|
||||
*/
|
||||
boolean tryLock(long time, TimeUnit unit,
|
||||
String key) throws InterruptedException;
|
||||
boolean tryLock(String key, TimeUnit timeUnit, long lockTime, long waitLockTime);
|
||||
|
||||
/**
|
||||
* 尝试加锁
|
||||
* 尝试加锁,只加锁一次
|
||||
* @param key key
|
||||
* @param timeUnit 时间单位
|
||||
* @param lockTime 加锁时间
|
||||
* @return 返回
|
||||
* @since 0.0.1
|
||||
*/
|
||||
boolean tryLock(String key, TimeUnit timeUnit, long lockTime);
|
||||
|
||||
/**
|
||||
* 尝试加锁,只加锁一次
|
||||
* @param key key
|
||||
* @param lockTime 加锁时间
|
||||
* @return 返回
|
||||
* @since 0.0.1
|
||||
*/
|
||||
boolean tryLock(String key, long lockTime);
|
||||
|
||||
/**
|
||||
* 尝试加锁,只加锁一次
|
||||
* @param key key
|
||||
* @return 返回
|
||||
* @since 0.0.1
|
||||
@@ -34,7 +52,8 @@ public interface ILock extends Lock {
|
||||
* 解锁
|
||||
* @param key key
|
||||
* @since 0.0.1
|
||||
* @return 是否释放锁成功
|
||||
*/
|
||||
void unlock(String key);
|
||||
boolean unlock(String key);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.github.houbb.lock.api.core;
|
||||
|
||||
/**
|
||||
* 分布式锁 KEY 格式化处理
|
||||
*
|
||||
* @author binbin.hou
|
||||
* @since 0.0.1
|
||||
*/
|
||||
public interface ILockKeyFormat {
|
||||
|
||||
/**
|
||||
* 对 key 进行格式化处理,避免缓存命名空间处理等问题
|
||||
*
|
||||
* @param formatContext 上下文
|
||||
* @return 返回
|
||||
* @since 1.2.0
|
||||
*/
|
||||
String format(ILockKeyFormatContext formatContext);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.github.houbb.lock.api.core;
|
||||
|
||||
/**
|
||||
* 分布式锁 KEY 格式化处理上下文
|
||||
*
|
||||
* @author binbin.hou
|
||||
* @since 0.0.1
|
||||
*/
|
||||
public interface ILockKeyFormatContext {
|
||||
|
||||
/**
|
||||
* 原始的 key
|
||||
*
|
||||
* @return 返回
|
||||
* @since 1.2.0
|
||||
*/
|
||||
String rawKey();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.github.houbb.lock.api.core;
|
||||
|
||||
/**
|
||||
* 释放锁失败处理类
|
||||
*
|
||||
* @author binbin.hou
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public interface ILockReleaseFailHandler {
|
||||
|
||||
/**
|
||||
* 处理
|
||||
* @param context 上下文
|
||||
* @since 1.2.0
|
||||
*/
|
||||
void handle(ILockReleaseFailHandlerContext context);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.github.houbb.lock.api.core;
|
||||
|
||||
/**
|
||||
* 释放锁失败处理类
|
||||
*
|
||||
* @author binbin.hou
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public interface ILockReleaseFailHandlerContext {
|
||||
|
||||
/**
|
||||
* 释放锁对应的 key
|
||||
* @return 结果
|
||||
* @since 1.2.0
|
||||
*/
|
||||
String key();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.github.houbb.lock.api.core;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 分布式锁接口定义
|
||||
* @author binbin.hou
|
||||
* @since 0.0.1
|
||||
*/
|
||||
public interface ILockSupport {
|
||||
|
||||
/**
|
||||
* 尝试加锁,如果失败,会一直尝试。
|
||||
*
|
||||
* 1. 如果等待时间小于等于0,则直接执行加锁
|
||||
* 2. 如果等待时间大于等于0,则尝试循环等待。
|
||||
*
|
||||
* @param context 上下文
|
||||
* @return 返回
|
||||
* @since 0.0.1
|
||||
*/
|
||||
boolean tryLock(final ILockSupportContext context);
|
||||
|
||||
/**
|
||||
* 解锁
|
||||
* @param context 上下文
|
||||
* @since 0.0.1
|
||||
* @return 结果
|
||||
*/
|
||||
boolean unlock(final ILockSupportContext context);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.github.houbb.lock.api.core;
|
||||
|
||||
import com.github.houbb.common.cache.api.service.ICommonCacheService;
|
||||
import com.github.houbb.id.api.Id;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 分布式锁接口定义
|
||||
* @author binbin.hou
|
||||
* @since 0.0.1
|
||||
*/
|
||||
public interface ILockSupportContext {
|
||||
|
||||
/**
|
||||
* 标识
|
||||
*
|
||||
* @return 标识策略
|
||||
* @since 0.0.4
|
||||
*/
|
||||
Id id();
|
||||
|
||||
/**
|
||||
* 缓存
|
||||
* @return 缓存策略
|
||||
* @since 0.0.4
|
||||
*/
|
||||
ICommonCacheService cache();
|
||||
|
||||
/**
|
||||
* 时间单位
|
||||
* @return 时间单位
|
||||
*/
|
||||
TimeUnit timeUnit();
|
||||
|
||||
/**
|
||||
* 加锁时间
|
||||
* @return 时间
|
||||
* @since 1.2.0
|
||||
*/
|
||||
long lockTime();
|
||||
|
||||
/**
|
||||
* 等待加锁时间
|
||||
* @return 等待时间
|
||||
* @since 1.2.0
|
||||
*/
|
||||
long waitLockTime();
|
||||
|
||||
/**
|
||||
* 缓存对应的 key
|
||||
* @return 结果
|
||||
* @since 1.2.0
|
||||
*/
|
||||
String key();
|
||||
|
||||
/**
|
||||
* 锁 key 格式化
|
||||
* @since 1.2.0
|
||||
* @return 格式化
|
||||
*/
|
||||
ILockKeyFormat lockKeyFormat();
|
||||
|
||||
/**
|
||||
* 锁释放失败处理类
|
||||
* @since 1.2.0
|
||||
* @return 失败处理
|
||||
*/
|
||||
ILockReleaseFailHandler lockReleaseFailHandler();
|
||||
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.github.houbb.lock.api.core;
|
||||
|
||||
/**
|
||||
* 读写锁定义接口
|
||||
* @author binbin.hou
|
||||
* @since 0.0.2
|
||||
*/
|
||||
public interface IReadWriteLock {
|
||||
|
||||
/**
|
||||
* 获取读锁
|
||||
* @since 0.0.2
|
||||
*/
|
||||
void lockRead();
|
||||
|
||||
/**
|
||||
* 释放读锁
|
||||
*/
|
||||
void unlockRead();
|
||||
|
||||
/**
|
||||
* 获取写锁
|
||||
* @since 0.0.2
|
||||
*/
|
||||
void lockWrite();
|
||||
|
||||
/**
|
||||
* 释放写锁
|
||||
*/
|
||||
void unlockWrite();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.github.houbb.lock.api.exception;
|
||||
|
||||
/**
|
||||
* 加锁运行时异常
|
||||
* @since 1.0.0
|
||||
* @author dh
|
||||
*/
|
||||
public class LockException extends RuntimeException {
|
||||
|
||||
public LockException() {
|
||||
}
|
||||
|
||||
public LockException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public LockException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public LockException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public LockException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package com.github.houbb.lock.api.support;
|
||||
|
||||
/**
|
||||
* 操作接口定义
|
||||
*
|
||||
* ps: 可以基于集中式数据库做操作
|
||||
*
|
||||
* @author binbin.hou
|
||||
* @since 0.0.3
|
||||
*/
|
||||
public interface IOperator {
|
||||
|
||||
/**
|
||||
* 尝试获取分布式锁
|
||||
*
|
||||
* @param lockKey 锁
|
||||
* @param requestId 请求标识
|
||||
* @param expireTimeMills 超期时间
|
||||
* @return 是否获取成功
|
||||
* @since 0.0.3
|
||||
*/
|
||||
boolean lock(String lockKey, String requestId, int expireTimeMills);
|
||||
|
||||
/**
|
||||
* 解锁
|
||||
* @param lockKey 锁 key
|
||||
* @param requestId 请求标识
|
||||
* @return 结果
|
||||
* @since 0.0.3
|
||||
*/
|
||||
boolean unlock(String lockKey, String requestId);
|
||||
|
||||
/**
|
||||
* 清空过期的锁
|
||||
*
|
||||
* 避免单个线程 unlock 失败,定时移除过期的锁。
|
||||
* @since 0.0.4
|
||||
*/
|
||||
void clearExpireLock();
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>lock</artifactId>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<version>0.0.4</version>
|
||||
<version>1.3.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -36,7 +36,15 @@
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>log-integration</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>common-cache-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>redis-config-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
package com.github.houbb.lock.core.bs;
|
||||
|
||||
import com.github.houbb.common.cache.api.service.ICommonCacheService;
|
||||
import com.github.houbb.heaven.util.common.ArgUtil;
|
||||
import com.github.houbb.id.api.Id;
|
||||
import com.github.houbb.id.core.core.Ids;
|
||||
import com.github.houbb.lock.api.core.ILock;
|
||||
import com.github.houbb.lock.api.support.IOperator;
|
||||
import com.github.houbb.lock.core.support.simple.SimpleLock;
|
||||
import com.github.houbb.wait.api.IWait;
|
||||
import com.github.houbb.wait.core.Waits;
|
||||
import com.github.houbb.lock.api.core.*;
|
||||
import com.github.houbb.lock.core.support.format.LockKeyFormat;
|
||||
import com.github.houbb.lock.core.support.handler.LockReleaseFailHandler;
|
||||
import com.github.houbb.lock.core.support.lock.LockSupportContext;
|
||||
import com.github.houbb.lock.core.support.lock.RedisLockSupport;
|
||||
import com.github.houbb.redis.config.core.factory.JedisRedisServiceFactory;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 锁引导类
|
||||
@@ -15,71 +19,49 @@ import com.github.houbb.wait.core.Waits;
|
||||
* @author binbin.hou
|
||||
* @since 0.0.4
|
||||
*/
|
||||
public final class LockBs {
|
||||
public final class LockBs implements ILock {
|
||||
|
||||
private LockBs(){}
|
||||
private LockBs() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空初始化延迟时间
|
||||
* @since 0.0.4
|
||||
*/
|
||||
private long clearInitDelaySeconds = 60;
|
||||
|
||||
/**
|
||||
* 清空初始化周期
|
||||
* @since 0.0.4
|
||||
*/
|
||||
private long clearPeriodSeconds = 60;
|
||||
|
||||
/**
|
||||
* 是否启用清空任务
|
||||
* @since 0.0.4
|
||||
*/
|
||||
private boolean enableClearTask = true;
|
||||
|
||||
/**
|
||||
* 锁等待
|
||||
* @since 0.0.1
|
||||
*/
|
||||
protected IWait waits = Waits.threadSleep();
|
||||
public static LockBs newInstance() {
|
||||
return new LockBs();
|
||||
}
|
||||
|
||||
/**
|
||||
* 标识策略
|
||||
*
|
||||
* @since 0.0.4
|
||||
*/
|
||||
protected Id id = Ids.uuid32();
|
||||
private Id id = Ids.uuid32();
|
||||
|
||||
/**
|
||||
* 操作策略
|
||||
* 缓存策略
|
||||
*
|
||||
* @since 0.0.4
|
||||
*/
|
||||
protected IOperator operator;
|
||||
private ICommonCacheService cache = JedisRedisServiceFactory.pooled("127.0.0.1", 6379);
|
||||
|
||||
public static LockBs newInstance(final IOperator operator) {
|
||||
return new LockBs().operator(operator);
|
||||
}
|
||||
/**
|
||||
* 锁支持策略
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private ILockSupport lockSupport = new RedisLockSupport();
|
||||
|
||||
public LockBs clearInitDelaySeconds(long clearInitDelaySeconds) {
|
||||
this.clearInitDelaySeconds = clearInitDelaySeconds;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* 锁 key 格式化
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
private ILockKeyFormat lockKeyFormat = new LockKeyFormat();
|
||||
|
||||
public LockBs clearPeriodSeconds(long clearPeriodSeconds) {
|
||||
this.clearPeriodSeconds = clearPeriodSeconds;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LockBs enableClearTask(boolean enableClearTask) {
|
||||
this.enableClearTask = enableClearTask;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LockBs waits(IWait waits) {
|
||||
ArgUtil.notNull(waits, "waits");
|
||||
|
||||
this.waits = waits;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* 锁释放失败处理类
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
private ILockReleaseFailHandler lockReleaseFailHandler = new LockReleaseFailHandler();
|
||||
|
||||
public LockBs id(Id id) {
|
||||
ArgUtil.notNull(id, "id");
|
||||
@@ -88,24 +70,81 @@ public final class LockBs {
|
||||
return this;
|
||||
}
|
||||
|
||||
public LockBs operator(IOperator operator) {
|
||||
ArgUtil.notNull(operator, "operator");
|
||||
public LockBs cache(ICommonCacheService cache) {
|
||||
ArgUtil.notNull(cache, "cache");
|
||||
|
||||
this.operator = operator;
|
||||
this.cache = cache;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ILock lock() {
|
||||
ArgUtil.notNull(operator, "operator");
|
||||
public LockBs lockSupport(ILockSupport lockSupport) {
|
||||
ArgUtil.notNull(lockSupport, "lockSupport");
|
||||
|
||||
return SimpleLock.newInstance()
|
||||
.waits(waits)
|
||||
.id(id)
|
||||
.operator(operator)
|
||||
.enableClearTask(enableClearTask)
|
||||
.clearInitDelaySeconds(clearInitDelaySeconds)
|
||||
.clearPeriodSeconds(clearPeriodSeconds)
|
||||
.init();
|
||||
this.lockSupport = lockSupport;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LockBs lockKeyFormat(ILockKeyFormat lockKeyFormat) {
|
||||
ArgUtil.notNull(lockKeyFormat, "lockKeyFormat");
|
||||
|
||||
this.lockKeyFormat = lockKeyFormat;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LockBs lockReleaseFailHandler(ILockReleaseFailHandler lockReleaseFailHandler) {
|
||||
ArgUtil.notNull(lockReleaseFailHandler, "lockReleaseFailHandler");
|
||||
|
||||
this.lockReleaseFailHandler = lockReleaseFailHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ILockReleaseFailHandler lockReleaseFailHandler() {
|
||||
return lockReleaseFailHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryLock(String key, TimeUnit timeUnit, long lockTime, long waitLockTime) {
|
||||
ILockSupportContext supportContext = buildLockSupportContext(key, timeUnit, lockTime, waitLockTime);
|
||||
return this.lockSupport.tryLock(supportContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryLock(String key, TimeUnit timeUnit, long lockTime) {
|
||||
return this.tryLock(key, timeUnit, lockTime, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryLock(String key, long lockTime) {
|
||||
return this.tryLock(key, TimeUnit.SECONDS, lockTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryLock(String key) {
|
||||
return this.tryLock(key, 60);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unlock(String key) {
|
||||
ILockSupportContext supportContext = buildLockSupportContext(key, TimeUnit.SECONDS, 0, 0);
|
||||
return this.lockSupport.unlock(supportContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建上下文
|
||||
*
|
||||
* @param key key
|
||||
* @param timeUnit 时间
|
||||
* @param lockTime 加锁时间
|
||||
* @param waitLockTime 等待加锁时间
|
||||
* @return 结果
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private ILockSupportContext buildLockSupportContext(String key, TimeUnit timeUnit, long lockTime, long waitLockTime) {
|
||||
ArgUtil.notEmpty(key, "key");
|
||||
ArgUtil.notNull(timeUnit, "timeUnit");
|
||||
|
||||
ILockSupportContext context = LockSupportContext.newInstance().id(id).cache(cache).lockKeyFormat(lockKeyFormat).lockReleaseFailHandler(lockReleaseFailHandler).key(key).timeUnit(timeUnit).lockTime(lockTime).waitLockTime(waitLockTime);
|
||||
return context;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,184 +0,0 @@
|
||||
package com.github.houbb.lock.core.core;
|
||||
|
||||
import com.github.houbb.heaven.util.common.ArgUtil;
|
||||
import com.github.houbb.id.api.Id;
|
||||
import com.github.houbb.id.core.core.Ids;
|
||||
import com.github.houbb.lock.api.core.ILock;
|
||||
import com.github.houbb.lock.api.support.IOperator;
|
||||
import com.github.houbb.lock.core.constant.LockConst;
|
||||
import com.github.houbb.wait.api.IWait;
|
||||
import com.github.houbb.wait.core.Waits;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
|
||||
/**
|
||||
* 抽象实现
|
||||
* @author binbin.hou
|
||||
* @since 0.0.1
|
||||
*/
|
||||
public abstract class AbstractLock implements ILock {
|
||||
|
||||
/**
|
||||
* 锁等待
|
||||
* @since 0.0.1
|
||||
*/
|
||||
protected IWait waits = Waits.threadSleep();
|
||||
|
||||
/**
|
||||
* 标识策略
|
||||
* @since 0.0.4
|
||||
*/
|
||||
protected Id id = Ids.uuid32();
|
||||
|
||||
/**
|
||||
* 操作策略
|
||||
* @since 0.0.4
|
||||
*/
|
||||
protected IOperator operator;
|
||||
|
||||
/**
|
||||
* 清空初始化延迟时间
|
||||
* @since 0.0.4
|
||||
*/
|
||||
private long clearInitDelaySeconds = 5;
|
||||
|
||||
/**
|
||||
* 清空初始化周期
|
||||
* @since 0.0.4
|
||||
*/
|
||||
private long clearPeriodSeconds = 5;
|
||||
|
||||
/**
|
||||
* 是否启用清空任务
|
||||
* @since 0.0.4
|
||||
*/
|
||||
private boolean enableClearTask = true;
|
||||
|
||||
public AbstractLock waits(IWait waits) {
|
||||
this.waits = waits;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AbstractLock id(Id id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AbstractLock operator(IOperator operator) {
|
||||
this.operator = operator;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AbstractLock clearInitDelaySeconds(long clearInitDelaySeconds) {
|
||||
this.clearInitDelaySeconds = clearInitDelaySeconds;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AbstractLock clearPeriodSeconds(long clearPeriodSeconds) {
|
||||
this.clearPeriodSeconds = clearPeriodSeconds;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AbstractLock enableClearTask(boolean enableClearTask) {
|
||||
this.enableClearTask = enableClearTask;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @since 0.0.4
|
||||
*/
|
||||
public AbstractLock init() {
|
||||
// 参数校验
|
||||
ArgUtil.notNull(operator, "operator");
|
||||
|
||||
// 初始化任务
|
||||
initClearExpireKey();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化清空任务
|
||||
* @since 0.0.6
|
||||
*/
|
||||
private void initClearExpireKey() {
|
||||
if(!enableClearTask) {
|
||||
return;
|
||||
}
|
||||
|
||||
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
|
||||
//5S 清理一次
|
||||
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
operator.clearExpireLock();
|
||||
}
|
||||
}, clearInitDelaySeconds, clearPeriodSeconds, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void lock() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lockInterruptibly() throws InterruptedException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryLock() {
|
||||
return tryLock(LockConst.DEFAULT_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlock() {
|
||||
unlock(LockConst.DEFAULT_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryLock(long time, TimeUnit unit, String key) throws InterruptedException {
|
||||
long startTimeMills = System.currentTimeMillis();
|
||||
|
||||
// 一次获取,直接成功
|
||||
boolean result = this.tryLock(key);
|
||||
if(result) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 时间判断
|
||||
if(time <= 0) {
|
||||
return false;
|
||||
}
|
||||
long durationMills = unit.toMillis(time);
|
||||
long endMills = startTimeMills + durationMills;
|
||||
|
||||
// 循环等待
|
||||
while (System.currentTimeMillis() < endMills) {
|
||||
result = tryLock(key);
|
||||
if(result) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 等待 1ms
|
||||
waits.wait(TimeUnit.MILLISECONDS, 1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
|
||||
return tryLock(time, unit, LockConst.DEFAULT_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Condition newCondition() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package com.github.houbb.lock.core.core;
|
||||
|
||||
import com.github.houbb.heaven.annotation.ThreadSafe;
|
||||
|
||||
/**
|
||||
* 无任何锁的操作
|
||||
*
|
||||
* @author binbin.hou
|
||||
* @since 0.0.3
|
||||
*/
|
||||
@ThreadSafe
|
||||
public class LockNone extends AbstractLock {
|
||||
|
||||
@Override
|
||||
public boolean tryLock(String key) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlock(String key) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
package com.github.houbb.lock.core.core;
|
||||
|
||||
import com.github.houbb.lock.api.core.IReadWriteLock;
|
||||
import com.github.houbb.log.integration.core.Log;
|
||||
import com.github.houbb.log.integration.core.LogFactory;
|
||||
|
||||
/**
|
||||
* 读写锁实现
|
||||
*
|
||||
* @author binbin.hou
|
||||
* @since 0.0.2
|
||||
*/
|
||||
public class LockReadWrite implements IReadWriteLock {
|
||||
|
||||
private static final Log log = LogFactory.getLog(LockReadWrite.class);
|
||||
|
||||
/**
|
||||
* 读次数统计
|
||||
*/
|
||||
private volatile int readCount = 0;
|
||||
|
||||
/**
|
||||
* 写次数统计
|
||||
*/
|
||||
private volatile int writeCount = 0;
|
||||
|
||||
/**
|
||||
* 获取读锁,读锁在写锁不存在的时候才能获取
|
||||
*
|
||||
* @since 0.0.2
|
||||
*/
|
||||
@Override
|
||||
public synchronized void lockRead() {
|
||||
try {
|
||||
// 写锁存在,需要wait
|
||||
while (!tryLockRead()) {
|
||||
wait();
|
||||
}
|
||||
|
||||
readCount++;
|
||||
} catch (InterruptedException e) {
|
||||
Thread.interrupted();
|
||||
// 忽略打断
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试获取读锁
|
||||
*
|
||||
* @return 是否成功
|
||||
* @since 0.0.2
|
||||
*/
|
||||
private boolean tryLockRead() {
|
||||
if (writeCount > 0) {
|
||||
log.debug("当前有写锁,获取读锁失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放读锁
|
||||
*
|
||||
* @since 0.0.2
|
||||
*/
|
||||
@Override
|
||||
public synchronized void unlockRead() {
|
||||
readCount--;
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取写锁
|
||||
*
|
||||
* @since 0.0.2
|
||||
*/
|
||||
@Override
|
||||
public synchronized void lockWrite() {
|
||||
try {
|
||||
// 写锁存在,需要wait
|
||||
while (!tryLockWrite()) {
|
||||
wait();
|
||||
}
|
||||
|
||||
// 此时已经不存在获取写锁的线程了,因此占坑,防止写锁饥饿
|
||||
writeCount++;
|
||||
} catch (InterruptedException e) {
|
||||
Thread.interrupted();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试获取写锁
|
||||
*
|
||||
* @return 是否成功
|
||||
* @since 0.0.2
|
||||
*/
|
||||
private boolean tryLockWrite() {
|
||||
if (writeCount > 0) {
|
||||
log.debug("当前有其他写锁,获取写锁失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 读锁
|
||||
if (readCount > 0) {
|
||||
log.debug("当前有其他读锁,获取写锁失败。");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放写锁
|
||||
*
|
||||
* @since 0.0.2
|
||||
*/
|
||||
@Override
|
||||
public synchronized void unlockWrite() {
|
||||
writeCount--;
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
package com.github.houbb.lock.core.core;
|
||||
|
||||
import com.github.houbb.lock.api.core.IReadWriteLock;
|
||||
import com.github.houbb.log.integration.core.Log;
|
||||
import com.github.houbb.log.integration.core.LogFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* 读写锁实现-保证释放锁时为锁的持有者
|
||||
*
|
||||
* @author binbin.hou
|
||||
* @since 0.0.2
|
||||
*/
|
||||
public class LockReadWriteOwner implements IReadWriteLock {
|
||||
|
||||
private static final Log log = LogFactory.getLog(LockReadWriteOwner.class);
|
||||
|
||||
/**
|
||||
* 如果使用类似 write 的方式,会导致读锁只能有一个。
|
||||
* 调整为使用 HashMap 存放读的信息
|
||||
*
|
||||
* @since 0.0.2
|
||||
*/
|
||||
private final Map<Thread, Integer> readCountMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* volatile 引用,保证线程间的可见性+易变性
|
||||
*
|
||||
* @since 0.0.2
|
||||
*/
|
||||
private final AtomicReference<Thread> writeOwner = new AtomicReference<>();
|
||||
|
||||
/**
|
||||
* 写次数统计
|
||||
*/
|
||||
private volatile int writeCount = 0;
|
||||
|
||||
/**
|
||||
* 获取读锁,读锁在写锁不存在的时候才能获取
|
||||
*
|
||||
* @since 0.0.2
|
||||
*/
|
||||
@Override
|
||||
public synchronized void lockRead() {
|
||||
try {
|
||||
// 写锁存在,需要wait
|
||||
while (!tryLockRead()) {
|
||||
log.debug("获取读锁失败,进入等待状态。");
|
||||
wait();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.interrupted();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试获取读锁
|
||||
*
|
||||
* 读锁之间是不互斥的,这里后续需要优化。
|
||||
*
|
||||
* @return 是否成功
|
||||
* @since 0.0.2
|
||||
*/
|
||||
private boolean tryLockRead() {
|
||||
if (writeCount > 0) {
|
||||
log.debug("当前有写锁,获取读锁失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
Thread currentThread = Thread.currentThread();
|
||||
// 次数暂时固定为1,后面如果实现可重入,这里可以改进。
|
||||
this.readCountMap.put(currentThread, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放读锁
|
||||
*
|
||||
* @since 0.0.2
|
||||
*/
|
||||
@Override
|
||||
public synchronized void unlockRead() {
|
||||
Thread currentThread = Thread.currentThread();
|
||||
Integer readCount = readCountMap.get(currentThread);
|
||||
|
||||
if (readCount == null) {
|
||||
throw new RuntimeException("当前线程未持有任何读锁,释放锁失败!");
|
||||
} else {
|
||||
log.debug("释放读锁,唤醒所有等待线程。");
|
||||
readCountMap.remove(currentThread);
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取写锁
|
||||
*
|
||||
* @since 0.0.2
|
||||
*/
|
||||
@Override
|
||||
public synchronized void lockWrite() {
|
||||
try {
|
||||
// 写锁存在,需要wait
|
||||
while (!tryLockWrite()) {
|
||||
wait();
|
||||
}
|
||||
|
||||
// 此时已经不存在获取写锁的线程了,因此占坑,防止写锁饥饿
|
||||
writeCount++;
|
||||
} catch (InterruptedException e) {
|
||||
Thread.interrupted();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试获取写锁
|
||||
*
|
||||
* @return 是否成功
|
||||
* @since 0.0.2
|
||||
*/
|
||||
private boolean tryLockWrite() {
|
||||
if (writeCount > 0) {
|
||||
log.debug("当前有其他写锁,获取写锁失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 读锁
|
||||
if (!readCountMap.isEmpty()) {
|
||||
log.debug("当前有其他读锁,获取写锁失败。");
|
||||
return false;
|
||||
}
|
||||
|
||||
Thread currentThread = Thread.currentThread();
|
||||
boolean result = writeOwner.compareAndSet(null, currentThread);
|
||||
log.debug("尝试获取写锁结果:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放写锁
|
||||
*
|
||||
* @since 0.0.2
|
||||
*/
|
||||
@Override
|
||||
public synchronized void unlockWrite() {
|
||||
boolean toNullResult = writeOwner.compareAndSet(Thread.currentThread(), null);
|
||||
|
||||
if (toNullResult) {
|
||||
writeCount--;
|
||||
log.debug("写锁释放,唤醒所有等待线程。");
|
||||
notifyAll();
|
||||
} else {
|
||||
throw new RuntimeException("释放写锁失败");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
package com.github.houbb.lock.core.core;
|
||||
|
||||
import com.github.houbb.lock.api.core.IReadWriteLock;
|
||||
import com.github.houbb.log.integration.core.Log;
|
||||
import com.github.houbb.log.integration.core.LogFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* 读写锁实现-可重入锁
|
||||
*
|
||||
* @author binbin.hou
|
||||
* @since 0.0.2
|
||||
*/
|
||||
public class LockReadWriteRe implements IReadWriteLock {
|
||||
|
||||
private static final Log log = LogFactory.getLog(LockReadWriteRe.class);
|
||||
|
||||
/**
|
||||
* 如果使用类似 write 的方式,会导致读锁只能有一个。
|
||||
* 调整为使用 HashMap 存放读的信息
|
||||
*
|
||||
* @since 0.0.2
|
||||
*/
|
||||
private final Map<Thread, Integer> readCountMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* volatile 引用,保证线程间的可见性+易变性
|
||||
*
|
||||
* @since 0.0.2
|
||||
*/
|
||||
private final AtomicReference<Thread> writeOwner = new AtomicReference<>();
|
||||
|
||||
/**
|
||||
* 写次数统计
|
||||
*/
|
||||
private volatile int writeCount = 0;
|
||||
|
||||
/**
|
||||
* 获取读锁,读锁在写锁不存在的时候才能获取
|
||||
*
|
||||
* @since 0.0.2
|
||||
*/
|
||||
@Override
|
||||
public synchronized void lockRead() {
|
||||
try {
|
||||
// 写锁存在,需要wait
|
||||
while (!tryLockRead()) {
|
||||
log.debug("获取读锁失败,进入等待状态。");
|
||||
wait();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.interrupted();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试获取读锁
|
||||
*
|
||||
* 读锁之间是不互斥的,这里后续需要优化。
|
||||
*
|
||||
* @return 是否成功
|
||||
* @since 0.0.2
|
||||
*/
|
||||
private boolean tryLockRead() {
|
||||
if (writeCount > 0) {
|
||||
log.debug("当前有写锁,获取读锁失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
Thread currentThread = Thread.currentThread();
|
||||
Integer count = readCountMap.get(currentThread);
|
||||
if(count == null) {
|
||||
count = 0;
|
||||
}
|
||||
count++;
|
||||
|
||||
this.readCountMap.put(currentThread, count);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放读锁
|
||||
*
|
||||
* @since 0.0.2
|
||||
*/
|
||||
@Override
|
||||
public synchronized void unlockRead() {
|
||||
Thread currentThread = Thread.currentThread();
|
||||
Integer readCount = readCountMap.get(currentThread);
|
||||
|
||||
if (readCount == null) {
|
||||
throw new RuntimeException("当前线程未持有任何读锁,释放锁失败!");
|
||||
} else {
|
||||
readCount--;
|
||||
|
||||
// 已经是最后一次
|
||||
if(readCount == 0) {
|
||||
readCountMap.remove(currentThread);
|
||||
} else {
|
||||
readCountMap.put(currentThread, readCount);
|
||||
}
|
||||
|
||||
log.debug("释放读锁,唤醒所有等待线程。");
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取写锁
|
||||
*
|
||||
* @since 0.0.2
|
||||
*/
|
||||
@Override
|
||||
public synchronized void lockWrite() {
|
||||
try {
|
||||
// 写锁存在,需要wait
|
||||
while (!tryLockWrite()) {
|
||||
log.debug("获取写锁失败,进入等待状态。");
|
||||
wait();
|
||||
}
|
||||
|
||||
// 此时已经不存在获取写锁的线程了,因此占坑,防止写锁饥饿
|
||||
writeCount++;
|
||||
} catch (InterruptedException e) {
|
||||
Thread.interrupted();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试获取写锁
|
||||
*
|
||||
* @return 是否成功
|
||||
* @since 0.0.2
|
||||
*/
|
||||
private boolean tryLockWrite() {
|
||||
if (writeCount > 0) {
|
||||
log.debug("当前有其他写锁,获取写锁失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 读锁
|
||||
if (!readCountMap.isEmpty()) {
|
||||
log.debug("当前有其他读锁,获取写锁失败。");
|
||||
return false;
|
||||
}
|
||||
|
||||
Thread currentThread = Thread.currentThread();
|
||||
// 多次重入
|
||||
if(writeOwner.get() == currentThread) {
|
||||
log.debug("为当前写线程多次重入,直接返回 true。");
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean result = writeOwner.compareAndSet(null, currentThread);
|
||||
log.debug("尝试获取写锁结果:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放写锁
|
||||
*
|
||||
* @since 0.0.2
|
||||
*/
|
||||
@Override
|
||||
public synchronized void unlockWrite() {
|
||||
Thread currentThread = Thread.currentThread();
|
||||
// 多次重入释放(当次数多于1时直接返回,否则需要释放 owner 信息)
|
||||
if(writeCount > 1 && (currentThread == writeOwner.get())) {
|
||||
log.debug("当前为写锁释放多次重入,直接返回成功。");
|
||||
|
||||
unlockWriteNotify();
|
||||
return;
|
||||
}
|
||||
|
||||
boolean toNullResult = writeOwner.compareAndSet(currentThread, null);
|
||||
if (toNullResult) {
|
||||
unlockWriteNotify();
|
||||
} else {
|
||||
throw new RuntimeException("释放写锁失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放写锁并且通知
|
||||
*/
|
||||
private synchronized void unlockWriteNotify() {
|
||||
writeCount--;
|
||||
log.debug("释放写锁成功,唤醒所有等待线程。");
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package com.github.houbb.lock.core.core;
|
||||
|
||||
import com.github.houbb.lock.core.exception.LockRuntimeException;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* 自旋锁
|
||||
* @author binbin.hou
|
||||
* @since 0.0.2
|
||||
*/
|
||||
public class LockSpin extends AbstractLock {
|
||||
|
||||
/**
|
||||
* volatile 引用,保证线程间的可见性+易变性
|
||||
*
|
||||
* @since 0.0.2
|
||||
*/
|
||||
private AtomicReference<Thread> owner =new AtomicReference<>();
|
||||
|
||||
@Override
|
||||
public void lock() {
|
||||
// 循环等待,直到获取到锁
|
||||
while (!tryLock()) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryLock(String key) {
|
||||
Thread current = Thread.currentThread();
|
||||
// CAS
|
||||
return owner.compareAndSet(null, current);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlock(String key) {
|
||||
Thread current = Thread.currentThread();
|
||||
boolean result = owner.compareAndSet(current, null);
|
||||
if(!result) {
|
||||
throw new LockRuntimeException("解锁失败");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package com.github.houbb.lock.core.core;
|
||||
|
||||
import com.github.houbb.heaven.util.util.DateUtil;
|
||||
import com.github.houbb.lock.core.exception.LockRuntimeException;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* 自旋锁-可重入
|
||||
* @author binbin.hou
|
||||
* @since 0.0.2
|
||||
*/
|
||||
public class LockSpinRe extends AbstractLock {
|
||||
|
||||
/**
|
||||
* volatile 引用,保证线程间的可见性+易变性
|
||||
*
|
||||
* @since 0.0.2
|
||||
*/
|
||||
private AtomicReference<Thread> owner =new AtomicReference<>();
|
||||
|
||||
/**
|
||||
* 计数统计类
|
||||
*
|
||||
* @since 0.0.2
|
||||
*/
|
||||
private AtomicLong count = new AtomicLong(0);
|
||||
|
||||
@Override
|
||||
public void lock() {
|
||||
// 循环等待,直到获取到锁
|
||||
while (!tryLock()) {
|
||||
// sleep
|
||||
DateUtil.sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryLock(String key) {
|
||||
Thread current = Thread.currentThread();
|
||||
// 判断是否已经拥有此锁
|
||||
if(current == owner.get()) {
|
||||
// 原子性自增 1
|
||||
count.incrementAndGet();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// CAS
|
||||
return owner.compareAndSet(null, current);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlock(String key) {
|
||||
Thread current = Thread.currentThread();
|
||||
|
||||
// 可重入实现
|
||||
if(owner.get() == current && count.get() != 0) {
|
||||
count.decrementAndGet();
|
||||
return;
|
||||
}
|
||||
|
||||
boolean result = owner.compareAndSet(current, null);
|
||||
if(!result) {
|
||||
throw new LockRuntimeException("解锁失败");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package com.github.houbb.lock.core.core;
|
||||
|
||||
import com.github.houbb.lock.core.exception.LockRuntimeException;
|
||||
import com.github.houbb.log.integration.core.Log;
|
||||
import com.github.houbb.log.integration.core.LogFactory;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* 等待通知的锁实现
|
||||
* @author binbin.hou
|
||||
* @since 0.0.2
|
||||
*/
|
||||
public class LockWaitNotify extends AbstractLock {
|
||||
|
||||
private static final Log log = LogFactory.getLog(LockWaitNotify.class);
|
||||
|
||||
/**
|
||||
* volatile 引用,保证线程间的可见性+易变性
|
||||
*
|
||||
* @since 0.0.2
|
||||
*/
|
||||
private AtomicReference<Thread> owner =new AtomicReference<>();
|
||||
|
||||
@Override
|
||||
public synchronized void lock() {
|
||||
while (!tryLock()) {
|
||||
try {
|
||||
log.debug("等待被唤醒");
|
||||
wait();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
// 是否可以被打断
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryLock(String key) {
|
||||
Thread current = Thread.currentThread();
|
||||
// CAS
|
||||
boolean result = owner.compareAndSet(null, current);
|
||||
log.debug("尝试获取锁结果:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void unlock(String key) {
|
||||
Thread current = Thread.currentThread();
|
||||
boolean result = owner.compareAndSet(current, null);
|
||||
if(!result) {
|
||||
throw new LockRuntimeException("解锁失败");
|
||||
}
|
||||
|
||||
// 唤醒等待中的线程
|
||||
log.debug("唤醒等待的进程");
|
||||
notify();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
package com.github.houbb.lock.core.core;
|
||||
|
||||
import com.github.houbb.lock.core.exception.LockRuntimeException;
|
||||
import com.github.houbb.log.integration.core.Log;
|
||||
import com.github.houbb.log.integration.core.LogFactory;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* 等待通知的锁实现-可重入
|
||||
* @author binbin.hou
|
||||
* @since 0.0.2
|
||||
*/
|
||||
public class LockWaitNotifyRe extends AbstractLock {
|
||||
|
||||
private static final Log log = LogFactory.getLog(LockWaitNotifyRe.class);
|
||||
|
||||
/**
|
||||
* volatile 引用,保证线程间的可见性+易变性
|
||||
*
|
||||
* @since 0.0.2
|
||||
*/
|
||||
private AtomicReference<Thread> owner =new AtomicReference<>();
|
||||
|
||||
/**
|
||||
* 次数统计
|
||||
* @since 0.0.2
|
||||
*/
|
||||
private AtomicInteger count = new AtomicInteger(0);
|
||||
|
||||
@Override
|
||||
public synchronized void lock() {
|
||||
while (!tryLock()) {
|
||||
try {
|
||||
log.debug("等待被唤醒");
|
||||
wait();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
// 是否可以被打断
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryLock(String key) {
|
||||
Thread current = Thread.currentThread();
|
||||
|
||||
//可重入实现
|
||||
if(current == owner.get()) {
|
||||
count.incrementAndGet();
|
||||
log.debug("当前线程已经拥有锁,直接返回 true");
|
||||
return true;
|
||||
}
|
||||
|
||||
// CAS
|
||||
boolean result = owner.compareAndSet(null, current);
|
||||
log.debug("尝试获取锁结果:{}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void unlock(String key) {
|
||||
Thread current = Thread.currentThread();
|
||||
|
||||
// 可重入实现
|
||||
if(owner.get() == current && count.get() != 0) {
|
||||
count.decrementAndGet();
|
||||
notifyAndLog();
|
||||
return;
|
||||
}
|
||||
|
||||
boolean result = owner.compareAndSet(current, null);
|
||||
if(!result) {
|
||||
throw new LockRuntimeException("解锁失败");
|
||||
}
|
||||
|
||||
notifyAndLog();
|
||||
}
|
||||
|
||||
private void notifyAndLog() {
|
||||
// 唤醒等待中的线程
|
||||
log.debug("唤醒等待的进程");
|
||||
notify();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
package com.github.houbb.lock.core.core;
|
||||
|
||||
import com.github.houbb.lock.api.core.ILock;
|
||||
import com.github.houbb.lock.api.core.IReadWriteLock;
|
||||
|
||||
/**
|
||||
* 锁工具
|
||||
*
|
||||
* @author binbin.hou
|
||||
* @since 0.0.3
|
||||
*/
|
||||
public final class Locks {
|
||||
|
||||
private Locks(){}
|
||||
|
||||
/**
|
||||
* 无锁
|
||||
* @return 锁
|
||||
* @since 0.0.3
|
||||
*/
|
||||
public static ILock none() {
|
||||
return new LockNone();
|
||||
}
|
||||
|
||||
/**
|
||||
* 读写锁
|
||||
* @return 锁
|
||||
* @since 0.0.3
|
||||
*/
|
||||
public static IReadWriteLock readWrite() {
|
||||
return new LockReadWrite();
|
||||
}
|
||||
|
||||
/**
|
||||
* 读写锁
|
||||
* @return 锁
|
||||
* @since 0.0.3
|
||||
*/
|
||||
public static IReadWriteLock readWriteOwner() {
|
||||
return new LockReadWriteOwner();
|
||||
}
|
||||
|
||||
/**
|
||||
* 可重入读写锁
|
||||
* @return 锁
|
||||
* @since 0.0.3
|
||||
*/
|
||||
public static IReadWriteLock readWriteRe() {
|
||||
return new LockReadWriteRe();
|
||||
}
|
||||
|
||||
/**
|
||||
* 自旋锁
|
||||
* @return 锁
|
||||
* @since 0.0.3
|
||||
*/
|
||||
public static ILock spin() {
|
||||
return new LockSpin();
|
||||
}
|
||||
|
||||
/**
|
||||
* 可重入自旋锁
|
||||
* @return 锁
|
||||
* @since 0.0.3
|
||||
*/
|
||||
public static ILock spinRe() {
|
||||
return new LockSpinRe();
|
||||
}
|
||||
|
||||
/**
|
||||
* 等待通知锁
|
||||
* @return 锁
|
||||
* @since 0.0.3
|
||||
*/
|
||||
public static ILock waitNotify() {
|
||||
return new LockWaitNotify();
|
||||
}
|
||||
|
||||
/**
|
||||
* 可重入等待通知锁
|
||||
* @return 锁
|
||||
* @since 0.0.3
|
||||
*/
|
||||
public static ILock waitNotifyRe() {
|
||||
return new LockWaitNotifyRe();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package com.github.houbb.lock.core.exception;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 0.0.2
|
||||
*/
|
||||
public class LockRuntimeException extends RuntimeException {
|
||||
|
||||
public LockRuntimeException() {
|
||||
}
|
||||
|
||||
public LockRuntimeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public LockRuntimeException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public LockRuntimeException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public LockRuntimeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.github.houbb.lock.core.support.format;
|
||||
|
||||
import com.github.houbb.lock.api.core.ILockKeyFormat;
|
||||
import com.github.houbb.lock.api.core.ILockKeyFormatContext;
|
||||
|
||||
/**
|
||||
* 简单的格式化处理
|
||||
* @since 1.2.0
|
||||
* @author dh
|
||||
*/
|
||||
public class LockKeyFormat implements ILockKeyFormat {
|
||||
|
||||
@Override
|
||||
public String format(ILockKeyFormatContext formatContext) {
|
||||
String rawKey = formatContext.rawKey();
|
||||
|
||||
String format = "DISTRIBUTED-LOCK:%s";
|
||||
return String.format(format, rawKey);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.github.houbb.lock.core.support.format;
|
||||
|
||||
import com.github.houbb.lock.api.core.ILockKeyFormatContext;
|
||||
|
||||
/**
|
||||
* 上下文
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class LockKeyFormatContext implements ILockKeyFormatContext {
|
||||
|
||||
|
||||
/**
|
||||
* 原始的 key
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
private String rawKey;
|
||||
|
||||
public static LockKeyFormatContext newInstance() {
|
||||
return new LockKeyFormatContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String rawKey() {
|
||||
return rawKey;
|
||||
}
|
||||
|
||||
public LockKeyFormatContext rawKey(String rawKey) {
|
||||
this.rawKey = rawKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.github.houbb.lock.core.support.handler;
|
||||
|
||||
import com.github.houbb.lock.api.core.ILockReleaseFailHandler;
|
||||
import com.github.houbb.lock.api.core.ILockReleaseFailHandlerContext;
|
||||
import com.github.houbb.log.integration.core.Log;
|
||||
import com.github.houbb.log.integration.core.LogFactory;
|
||||
|
||||
/**
|
||||
* 释放锁失败处理类
|
||||
*
|
||||
* @author binbin.hou
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class LockReleaseFailHandler implements ILockReleaseFailHandler {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(LockReleaseFailHandler.class);
|
||||
|
||||
@Override
|
||||
public void handle(ILockReleaseFailHandlerContext context) {
|
||||
LOG.error("[LOCK] release lock failed {}", context.key());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.github.houbb.lock.core.support.handler;
|
||||
|
||||
import com.github.houbb.lock.api.core.ILockReleaseFailHandlerContext;
|
||||
|
||||
/**
|
||||
* 锁释放失败上下文
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class LockReleaseFailHandlerContext implements ILockReleaseFailHandlerContext {
|
||||
|
||||
/**
|
||||
* 释放锁对应的 key
|
||||
* @since 1.2.0
|
||||
*/
|
||||
private String key;
|
||||
|
||||
public static LockReleaseFailHandlerContext newInstance() {
|
||||
return new LockReleaseFailHandlerContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String key() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public LockReleaseFailHandlerContext key(String key) {
|
||||
this.key = key;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package com.github.houbb.lock.core.support.lock;
|
||||
|
||||
import com.github.houbb.common.cache.api.service.ICommonCacheService;
|
||||
import com.github.houbb.id.api.Id;
|
||||
import com.github.houbb.lock.api.core.ILockKeyFormat;
|
||||
import com.github.houbb.lock.api.core.ILockReleaseFailHandler;
|
||||
import com.github.houbb.lock.api.core.ILockSupportContext;
|
||||
import com.github.houbb.lock.core.support.format.LockKeyFormat;
|
||||
import com.github.houbb.lock.core.support.handler.LockReleaseFailHandler;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 分布式锁接口定义
|
||||
* @author binbin.hou
|
||||
* @since 0.0.1
|
||||
*/
|
||||
public class LockSupportContext implements ILockSupportContext {
|
||||
|
||||
public static LockSupportContext newInstance() {
|
||||
return new LockSupportContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* 标识策略
|
||||
* @since 0.0.4
|
||||
*/
|
||||
private Id id;
|
||||
|
||||
/**
|
||||
* 缓存策略
|
||||
* @since 0.0.4
|
||||
*/
|
||||
private ICommonCacheService cache;
|
||||
|
||||
/**
|
||||
* 时间单位
|
||||
*/
|
||||
private TimeUnit timeUnit;
|
||||
|
||||
/**
|
||||
* 加锁时间
|
||||
* @since 1.2.0
|
||||
*/
|
||||
private long lockTime;
|
||||
|
||||
/**
|
||||
* 等待加锁时间
|
||||
* @since 1.2.0
|
||||
*/
|
||||
private long waitLockTime;
|
||||
|
||||
/**
|
||||
* 缓存对应的 key
|
||||
* @since 1.2.0
|
||||
*/
|
||||
private String key;
|
||||
|
||||
/**
|
||||
* 锁 key 格式化
|
||||
* @since 1.2.0
|
||||
*/
|
||||
private ILockKeyFormat lockKeyFormat;
|
||||
|
||||
/**
|
||||
* 锁释放失败处理类
|
||||
* @since 1.2.0
|
||||
*/
|
||||
private ILockReleaseFailHandler lockReleaseFailHandler;
|
||||
|
||||
|
||||
@Override
|
||||
public Id id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public LockSupportContext id(Id id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICommonCacheService cache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
public LockSupportContext cache(ICommonCacheService cache) {
|
||||
this.cache = cache;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeUnit timeUnit() {
|
||||
return timeUnit;
|
||||
}
|
||||
|
||||
public LockSupportContext timeUnit(TimeUnit timeUnit) {
|
||||
this.timeUnit = timeUnit;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long lockTime() {
|
||||
return lockTime;
|
||||
}
|
||||
|
||||
public LockSupportContext lockTime(long lockTime) {
|
||||
this.lockTime = lockTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long waitLockTime() {
|
||||
return waitLockTime;
|
||||
}
|
||||
|
||||
public LockSupportContext waitLockTime(long waitLockTime) {
|
||||
this.waitLockTime = waitLockTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String key() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public LockSupportContext key(String key) {
|
||||
this.key = key;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILockKeyFormat lockKeyFormat() {
|
||||
return lockKeyFormat;
|
||||
}
|
||||
|
||||
public LockSupportContext lockKeyFormat(ILockKeyFormat lockKeyFormat) {
|
||||
this.lockKeyFormat = lockKeyFormat;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILockReleaseFailHandler lockReleaseFailHandler() {
|
||||
return lockReleaseFailHandler;
|
||||
}
|
||||
|
||||
public LockSupportContext lockReleaseFailHandler(ILockReleaseFailHandler lockReleaseFailHandler) {
|
||||
this.lockReleaseFailHandler = lockReleaseFailHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.github.houbb.lock.core.support.lock;
|
||||
|
||||
import com.github.houbb.common.cache.api.service.ICommonCacheService;
|
||||
import com.github.houbb.heaven.util.lang.StringUtil;
|
||||
import com.github.houbb.id.api.Id;
|
||||
import com.github.houbb.id.core.util.IdThreadLocalHelper;
|
||||
import com.github.houbb.lock.api.core.ILockKeyFormat;
|
||||
import com.github.houbb.lock.api.core.ILockSupport;
|
||||
import com.github.houbb.lock.api.core.ILockSupportContext;
|
||||
import com.github.houbb.lock.core.support.format.LockKeyFormatContext;
|
||||
import com.github.houbb.log.integration.core.Log;
|
||||
import com.github.houbb.log.integration.core.LogFactory;
|
||||
import com.github.houbb.redis.config.core.constant.JedisConst;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 分布式锁接口定义
|
||||
* @author binbin.hou
|
||||
* @since 0.0.1
|
||||
*/
|
||||
public class RedisLockSupport implements ILockSupport {
|
||||
|
||||
private final Log log = LogFactory.getLog(RedisLockSupport.class);
|
||||
|
||||
@Override
|
||||
public boolean tryLock(ILockSupportContext context) {
|
||||
long startTimeMills = System.currentTimeMillis();
|
||||
|
||||
// 一次获取,直接成功
|
||||
boolean result = this.doLock(context);
|
||||
if(result) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 时间判断
|
||||
final TimeUnit timeUnit = context.timeUnit();
|
||||
final long waitLockTime = context.waitLockTime();
|
||||
if(waitLockTime <= 0) {
|
||||
return false;
|
||||
}
|
||||
long durationMills = timeUnit.toMillis(waitLockTime);
|
||||
long endMills = startTimeMills + durationMills;
|
||||
|
||||
// 循环等待
|
||||
while (System.currentTimeMillis() < endMills) {
|
||||
result = this.doLock(context);
|
||||
if(result) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 等待 1ms
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
log.debug("[LOCK] try lock wait 1 mills.");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行加锁
|
||||
* @param context 上下文
|
||||
* @return 结果
|
||||
*/
|
||||
protected boolean doLock(ILockSupportContext context) {
|
||||
final String key = this.getActualKey(context);
|
||||
|
||||
// 生成当前线程的唯一标识
|
||||
final Id id = context.id();
|
||||
final String requestId = id.id();
|
||||
IdThreadLocalHelper.put(requestId);
|
||||
log.info("[LOCK] BEGIN TRY LOCK key: {} requestId: {}", key, requestId);
|
||||
|
||||
final ICommonCacheService commonCacheService = context.cache();
|
||||
final TimeUnit timeUnit = context.timeUnit();
|
||||
final long lockTime = context.lockTime();
|
||||
final int lockExpireMills = (int) timeUnit.toMillis(lockTime);
|
||||
|
||||
String result = commonCacheService.set(key, requestId, JedisConst.SET_IF_NOT_EXIST, JedisConst.SET_WITH_EXPIRE_TIME, lockExpireMills);
|
||||
log.info("[LOCK] END TRY LOCK key: {}, requestId: {}, lockExpireMills: {}, result: {}", key, requestId, lockExpireMills, result);
|
||||
return JedisConst.OK.equalsIgnoreCase(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unlock(ILockSupportContext context) {
|
||||
final String key = this.getActualKey(context);
|
||||
|
||||
String requestId = IdThreadLocalHelper.get();
|
||||
log.info("[LOCK] BEGIN UN LOCK key: {} requestId: {}", key, requestId);
|
||||
|
||||
if(StringUtil.isEmpty(requestId)) {
|
||||
log.warn("[LOCK] UNLOCK requestId not found, ignore");
|
||||
return false;
|
||||
}
|
||||
|
||||
final ICommonCacheService commonCacheService = context.cache();
|
||||
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
|
||||
Object result = commonCacheService.eval(script, Collections.singletonList(key), Collections.singletonList(requestId));
|
||||
log.info("[LOCK] END UN LOCK key: {}, requestId: {}, result: {}", key, requestId, result);
|
||||
return JedisConst.RELEASE_SUCCESS.equals(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建真正的 key
|
||||
* @param context 上下文
|
||||
* @return 结果
|
||||
* @since 1.2.0
|
||||
*/
|
||||
private String getActualKey(ILockSupportContext context) {
|
||||
final String rawKey = context.key();
|
||||
final ILockKeyFormat keyFormat = context.lockKeyFormat();
|
||||
LockKeyFormatContext formatContext = LockKeyFormatContext.newInstance()
|
||||
.rawKey(rawKey);
|
||||
|
||||
String key = keyFormat.format(formatContext);
|
||||
log.info("[LOCK] format rawKey: {} to key: {}", rawKey, key);
|
||||
return key;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package com.github.houbb.lock.core.support.simple;
|
||||
|
||||
import com.github.houbb.heaven.util.lang.StringUtil;
|
||||
import com.github.houbb.id.core.util.IdThreadLocalHelper;
|
||||
import com.github.houbb.lock.core.constant.LockConst;
|
||||
import com.github.houbb.lock.core.core.AbstractLock;
|
||||
import com.github.houbb.lock.core.exception.LockRuntimeException;
|
||||
|
||||
/**
|
||||
* 简单锁实现策略
|
||||
*
|
||||
* @author binbin.hou
|
||||
* @since 0.0.4
|
||||
*/
|
||||
public class SimpleLock extends AbstractLock {
|
||||
|
||||
public static SimpleLock newInstance() {
|
||||
return new SimpleLock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryLock(String key) {
|
||||
final String requestId = id.id();
|
||||
IdThreadLocalHelper.put(requestId);
|
||||
|
||||
return operator.lock(key, requestId, LockConst.DEFAULT_EXPIRE_MILLS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlock(String key) {
|
||||
final String requestId = IdThreadLocalHelper.get();
|
||||
if(StringUtil.isEmpty(requestId)) {
|
||||
String threadName = Thread.currentThread().getName();
|
||||
throw new LockRuntimeException("Thread " + threadName +" not contains requestId");
|
||||
}
|
||||
|
||||
boolean unlock = operator.unlock(key, requestId);
|
||||
if(!unlock) {
|
||||
throw new LockRuntimeException("Unlock key " + key + " result is failed!");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package com.github.houbb.lock.redis.constant;
|
||||
|
||||
/**
|
||||
* redis 锁常量
|
||||
*
|
||||
* @author binbin.hou
|
||||
* @since 0.0.1
|
||||
*/
|
||||
public final class LockRedisConst {
|
||||
|
||||
private LockRedisConst() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 加锁成功
|
||||
* @since 0.0.1
|
||||
*/
|
||||
public static final String LOCK_SUCCESS = "OK";
|
||||
|
||||
/**
|
||||
* 如果不存在则设置值
|
||||
* @since 0.0.1
|
||||
*/
|
||||
public static final String SET_IF_NOT_EXIST = "NX";
|
||||
|
||||
/**
|
||||
* 设置过期时间
|
||||
*
|
||||
* 单位:milliseconds
|
||||
* @since 0.0.1
|
||||
*/
|
||||
public static final String SET_WITH_EXPIRE_TIME = "PX";
|
||||
|
||||
/**
|
||||
* 解锁成功
|
||||
*
|
||||
* @since 0.0.1
|
||||
*/
|
||||
public static final Long RELEASE_SUCCESS = 1L;
|
||||
|
||||
|
||||
/**
|
||||
* 默认的失效时间
|
||||
*
|
||||
* 暂时定为 30min
|
||||
* @since 0.0.1
|
||||
*/
|
||||
public static final int DEFAULT_EXPIRE_MILLS = 1000 * 60 * 30;
|
||||
|
||||
/**
|
||||
* 默认锁为全局锁
|
||||
* @since 0.0.1
|
||||
*/
|
||||
public static final String DEFAULT_KEY = "GLOBAL";
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package com.github.houbb.lock.redis.exception;
|
||||
|
||||
import com.github.houbb.lock.core.exception.LockRuntimeException;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 0.0.3
|
||||
*/
|
||||
public class LockRedisException extends LockRuntimeException {
|
||||
|
||||
public LockRedisException() {
|
||||
}
|
||||
|
||||
public LockRedisException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public LockRedisException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public LockRedisException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public LockRedisException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
package com.github.houbb.lock.redis.exception;
|
||||
@@ -1,5 +0,0 @@
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
package com.github.houbb.lock.redis;
|
||||
@@ -1,68 +0,0 @@
|
||||
package com.github.houbb.lock.redis.support.operator;
|
||||
|
||||
import com.github.houbb.lock.api.support.IOperator;
|
||||
import com.github.houbb.lock.redis.constant.LockRedisConst;
|
||||
import redis.clients.jedis.Jedis;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* Redis 客户端
|
||||
* @author binbin.hou
|
||||
* @since 0.0.1
|
||||
*/
|
||||
public class JedisOperator implements IOperator {
|
||||
|
||||
/**
|
||||
* jedis 客户端
|
||||
* @since 0.0.1
|
||||
*/
|
||||
private final Jedis jedis;
|
||||
|
||||
public JedisOperator(Jedis jedis) {
|
||||
this.jedis = jedis;
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试获取分布式锁
|
||||
*
|
||||
* expireTimeMills 保证当前进程挂掉,也能释放锁
|
||||
*
|
||||
* requestId 保证解锁的是当前进程(锁的持有者)
|
||||
*
|
||||
* @param lockKey 锁
|
||||
* @param requestId 请求标识
|
||||
* @param expireTimeMills 超期时间
|
||||
* @return 是否获取成功
|
||||
* @since 0.0.1
|
||||
*/
|
||||
@Override
|
||||
public boolean lock(String lockKey, String requestId, int expireTimeMills) {
|
||||
String result = jedis.set(lockKey, requestId, LockRedisConst.SET_IF_NOT_EXIST, LockRedisConst.SET_WITH_EXPIRE_TIME, expireTimeMills);
|
||||
return LockRedisConst.LOCK_SUCCESS.equals(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解锁
|
||||
*
|
||||
* (1)使用 requestId,保证为当前锁的持有者
|
||||
* (2)使用 lua 脚本,保证执行的原子性。
|
||||
*
|
||||
* @param lockKey 锁 key
|
||||
* @param requestId 请求标识
|
||||
* @return 结果
|
||||
* @since 0.0.1
|
||||
*/
|
||||
@Override
|
||||
public boolean unlock(String lockKey, String requestId) {
|
||||
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
|
||||
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
|
||||
return LockRedisConst.RELEASE_SUCCESS.equals(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearExpireLock() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
package com.github.houbb.lock.redis.support;
|
||||
35
lock-spring/pom.xml
Normal file
35
lock-spring/pom.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>lock</artifactId>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<version>1.3.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>lock-spring</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>lock-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>aop-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>aop-spring</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>redis-config-spring</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.github.houbb.lock.spring.annotation;
|
||||
|
||||
import com.github.houbb.lock.spring.config.LockAopConfig;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 启用注解
|
||||
* @author binbin.hou
|
||||
* @since 0.0.2
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Import(LockAopConfig.class)
|
||||
@EnableAspectJAutoProxy
|
||||
public @interface EnableLock {
|
||||
|
||||
/**
|
||||
* 唯一标识生成策略
|
||||
* @return 结果
|
||||
* @since 1.1.0
|
||||
*/
|
||||
String id() default "lockId";
|
||||
|
||||
/**
|
||||
* 缓存实现策略 bean 名称
|
||||
*
|
||||
* 默认引入 redis-config 中的配置
|
||||
*
|
||||
* @return 实现
|
||||
* @since 1.1.0
|
||||
*/
|
||||
String cache() default "springRedisService";
|
||||
|
||||
/**
|
||||
* 加锁 key 格式化策略
|
||||
* @return 策略
|
||||
* @since 1.2.0
|
||||
*/
|
||||
String lockKeyFormat() default "lockKeyFormat";
|
||||
|
||||
/**
|
||||
* 锁释放失败处理类
|
||||
* @return 结果
|
||||
* @since 1.2.0
|
||||
*/
|
||||
String lockReleaseFailHandler() default "lockReleaseFailHandler";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.github.houbb.lock.spring.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 分布式加锁注解
|
||||
* @author binbin.hou
|
||||
* @since 0.0.2
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
public @interface Lock {
|
||||
|
||||
/**
|
||||
* 缓存的 key 策略,支持 SpEL
|
||||
* @return 结果
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* 时间单位
|
||||
* @return 单位
|
||||
* @since 1.2.0
|
||||
*/
|
||||
TimeUnit timeUnit() default TimeUnit.SECONDS;
|
||||
|
||||
/**
|
||||
* 等待锁时间
|
||||
* @return 等待锁时间
|
||||
* @since 1.2.0
|
||||
*/
|
||||
long waitLockTime() default 10;
|
||||
|
||||
/**
|
||||
* 业务加锁时间
|
||||
* @return 加锁时间
|
||||
* @since 1.2.0
|
||||
*/
|
||||
long lockTime() default 60;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package com.github.houbb.lock.spring.aop;
|
||||
|
||||
import com.github.houbb.aop.spring.util.SpringAopUtil;
|
||||
import com.github.houbb.heaven.util.lang.ObjectUtil;
|
||||
import com.github.houbb.heaven.util.lang.StringUtil;
|
||||
import com.github.houbb.heaven.util.util.ArrayUtil;
|
||||
import com.github.houbb.lock.api.exception.LockException;
|
||||
import com.github.houbb.lock.core.bs.LockBs;
|
||||
import com.github.houbb.lock.core.support.handler.LockReleaseFailHandlerContext;
|
||||
import com.github.houbb.lock.spring.annotation.Lock;
|
||||
import com.github.houbb.log.integration.core.Log;
|
||||
import com.github.houbb.log.integration.core.LogFactory;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.Signature;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class LockAspect {
|
||||
|
||||
private final Log log = LogFactory.getLog(LockAspect.class);
|
||||
|
||||
@Autowired
|
||||
@Qualifier("lockBs")
|
||||
private LockBs lockBs;
|
||||
|
||||
@Pointcut("@annotation(com.github.houbb.lock.spring.annotation.Lock)")
|
||||
public void lockPointcut() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行核心方法
|
||||
*
|
||||
* 相当于 MethodInterceptor
|
||||
* @param point 切点
|
||||
* @return 结果
|
||||
* @throws Throwable 异常信息
|
||||
* @since 0.0.2
|
||||
*/
|
||||
@Around("lockPointcut()")
|
||||
public Object around(ProceedingJoinPoint point) throws Throwable {
|
||||
Method method = SpringAopUtil.getCurrentMethod(point);
|
||||
|
||||
boolean isLockAnnotation = method.isAnnotationPresent(Lock.class);
|
||||
if(isLockAnnotation) {
|
||||
Lock lockAnnotation = method.getAnnotation(Lock.class);
|
||||
|
||||
// 如果构建 key?
|
||||
String lockKey = buildLockKey(lockAnnotation, point);
|
||||
|
||||
boolean tryLockFlag = false;
|
||||
try {
|
||||
tryLockFlag = lockBs.tryLock(lockKey, lockAnnotation.timeUnit(), lockAnnotation.lockTime(), lockAnnotation.waitLockTime());
|
||||
if(!tryLockFlag) {
|
||||
log.warn("[LOCK] TRY LOCK FAILED {}", lockKey);
|
||||
throw new LockException("[LOCK] TRY LOCK FAILED " + lockKey);
|
||||
}
|
||||
|
||||
// 执行业务
|
||||
return point.proceed();
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
// 只有获取锁的情况下,才尝试释放锁
|
||||
if(tryLockFlag) {
|
||||
boolean unLockFlag = lockBs.unlock(lockKey);
|
||||
|
||||
// 释放锁结果
|
||||
this.lockReleaseFailHandle(unLockFlag, lockKey);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return point.proceed();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 锁释放失败
|
||||
* @param unLockFlag 释放结果
|
||||
* @param lockKey 加锁信息
|
||||
*/
|
||||
private void lockReleaseFailHandle(boolean unLockFlag,
|
||||
String lockKey) {
|
||||
if(unLockFlag) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 触发通知,便于用户自定义处理。
|
||||
// 比如报警
|
||||
LockReleaseFailHandlerContext handlerContext = LockReleaseFailHandlerContext.newInstance()
|
||||
.key(lockKey);
|
||||
this.lockBs.lockReleaseFailHandler().handle(handlerContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建加锁的 key
|
||||
*
|
||||
* https://www.cnblogs.com/fashflying/p/6908028.html spring cache
|
||||
*
|
||||
* https://www.cnblogs.com/best/p/5748105.html SpEL
|
||||
* @param lock 注解信息
|
||||
* @param joinPoint 参数
|
||||
* @return 结果
|
||||
*/
|
||||
private String buildLockKey(Lock lock,
|
||||
ProceedingJoinPoint joinPoint) {
|
||||
final Object[] args = joinPoint.getArgs();
|
||||
final String lockValue = lock.value();
|
||||
|
||||
//创建SpEL表达式的解析器
|
||||
ExpressionParser parser = new SpelExpressionParser();
|
||||
//1. 如果没有入参怎么办?
|
||||
if(ArrayUtil.isEmpty(args)) {
|
||||
log.warn("[LOCK] method args is empty, return lock.value() {}", lockValue);
|
||||
return lockValue;
|
||||
}
|
||||
|
||||
// 如果 lockValue 的值为空呢?
|
||||
if(StringUtil.isEmpty(lockValue)) {
|
||||
return Arrays.toString(args);
|
||||
}
|
||||
|
||||
// 如何获取参数名称呢?
|
||||
|
||||
//解析表达式需要的上下文,解析时有一个默认的上下文
|
||||
// jdk1.7 之前,直接使用 arg0, arg1...
|
||||
EvaluationContext ctx = new StandardEvaluationContext();
|
||||
|
||||
// 利用 spring 的处理方式
|
||||
Signature signature = joinPoint.getSignature();
|
||||
MethodSignature methodSignature = (MethodSignature) signature;
|
||||
String[] paramNameList = methodSignature.getParameterNames();
|
||||
|
||||
for(int i = 0; i < paramNameList.length; i++) {
|
||||
String paramName = paramNameList[i];
|
||||
Object paramValue = args[i];
|
||||
|
||||
//在上下文中设置变量,变量名为user,内容为user对象
|
||||
ctx.setVariable(paramName, paramValue);
|
||||
}
|
||||
|
||||
// 直接 toString,比较简单。
|
||||
Object value = parser.parseExpression(lockValue).getValue(ctx);
|
||||
return ObjectUtil.objectToString(value, "null");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.github.houbb.lock.spring.config;
|
||||
|
||||
import com.github.houbb.common.cache.api.service.ICommonCacheService;
|
||||
import com.github.houbb.id.api.Id;
|
||||
import com.github.houbb.lock.api.core.ILockKeyFormat;
|
||||
import com.github.houbb.lock.api.core.ILockReleaseFailHandler;
|
||||
import com.github.houbb.lock.core.bs.LockBs;
|
||||
import com.github.houbb.lock.spring.annotation.EnableLock;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.annotation.*;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
|
||||
/**
|
||||
* aop 配置
|
||||
*
|
||||
* @author binbin.hou
|
||||
* @since 0.0.2
|
||||
*/
|
||||
@Configuration
|
||||
@ComponentScan(basePackages = "com.github.houbb.lock.spring")
|
||||
@Import({LockBeanConfig.class})
|
||||
public class LockAopConfig implements ImportAware, BeanFactoryPostProcessor {
|
||||
|
||||
@Bean("lockBs")
|
||||
public LockBs lockBs() {
|
||||
ICommonCacheService commonCacheService = beanFactory.getBean(enableLockAttributes.getString("cache"), ICommonCacheService.class);
|
||||
Id id = beanFactory.getBean(enableLockAttributes.getString("id"), Id.class);
|
||||
ILockKeyFormat lockKeyFormat = beanFactory.getBean(enableLockAttributes.getString("lockKeyFormat"), ILockKeyFormat.class);
|
||||
ILockReleaseFailHandler lockReleaseFailHandler = beanFactory.getBean(enableLockAttributes.getString("lockReleaseFailHandler"), ILockReleaseFailHandler.class);
|
||||
|
||||
return LockBs.newInstance()
|
||||
.cache(commonCacheService)
|
||||
.id(id)
|
||||
.lockKeyFormat(lockKeyFormat)
|
||||
.lockReleaseFailHandler(lockReleaseFailHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 属性信息
|
||||
*/
|
||||
private AnnotationAttributes enableLockAttributes;
|
||||
|
||||
/**
|
||||
* bean 工厂
|
||||
*
|
||||
* @since 0.0.5
|
||||
*/
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImportMetadata(AnnotationMetadata annotationMetadata) {
|
||||
enableLockAttributes = AnnotationAttributes.fromMap(
|
||||
annotationMetadata.getAnnotationAttributes(EnableLock.class.getName(), false));
|
||||
if (enableLockAttributes == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"@EnableLock is not present on importing class " + annotationMetadata.getClassName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.github.houbb.lock.spring.config;
|
||||
|
||||
import com.github.houbb.id.api.Id;
|
||||
import com.github.houbb.id.core.core.Ids;
|
||||
import com.github.houbb.lock.api.core.ILockKeyFormat;
|
||||
import com.github.houbb.lock.api.core.ILockReleaseFailHandler;
|
||||
import com.github.houbb.lock.core.support.format.LockKeyFormat;
|
||||
import com.github.houbb.lock.core.support.handler.LockReleaseFailHandler;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* bean 配置
|
||||
*
|
||||
* @author binbin.hou
|
||||
* @since 0.0.2
|
||||
*/
|
||||
@Configuration
|
||||
@ComponentScan(basePackages = "com.github.houbb.lock.spring")
|
||||
public class LockBeanConfig {
|
||||
|
||||
@Bean("lockId")
|
||||
public Id lockId() {
|
||||
return Ids.uuid32();
|
||||
}
|
||||
|
||||
@Bean("lockKeyFormat")
|
||||
public ILockKeyFormat lockKeyFormat() {
|
||||
return new LockKeyFormat();
|
||||
}
|
||||
|
||||
@Bean("lockReleaseFailHandler")
|
||||
public ILockReleaseFailHandler lockReleaseFailHandler() {
|
||||
return new LockReleaseFailHandler();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package com.github.houbb.lock.spring;
|
||||
@@ -5,24 +5,27 @@
|
||||
<parent>
|
||||
<artifactId>lock</artifactId>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<version>0.0.4</version>
|
||||
<version>1.3.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>lock-redis</artifactId>
|
||||
<artifactId>lock-springboot-starter</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<!--============================== SELF ==============================-->
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>lock-core</artifactId>
|
||||
<artifactId>lock-spring</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--============================== OTHER ==============================-->
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>redis-config-springboot-starter</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.github.houbb.lock.springboot.starter.config;
|
||||
|
||||
import com.github.houbb.lock.spring.annotation.EnableLock;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 分布式锁自动配置
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(LockAutoConfig.class)
|
||||
@EnableLock
|
||||
public class LockAutoConfig {
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* @author d
|
||||
* @since 1.0.0
|
||||
*/
|
||||
package com.github.houbb.lock.springboot.starter;
|
||||
@@ -0,0 +1 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.github.houbb.lock.springboot.starter.config.LockAutoConfig
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>lock</artifactId>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<version>0.0.4</version>
|
||||
<version>1.3.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -18,13 +18,35 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>lock-redis</artifactId>
|
||||
<artifactId>lock-spring</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>test-spring</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>slf4j-common</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<version>1.1.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.1.11</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.github.houbb.lock.test.config;
|
||||
|
||||
|
||||
import com.github.houbb.lock.spring.annotation.EnableLock;
|
||||
import com.github.houbb.redis.config.spring.annotation.EnableRedisConfig;
|
||||
import org.springframework.beans.factory.annotation.Configurable;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configurable
|
||||
@ComponentScan(basePackages = "com.github.houbb.lock.test.service")
|
||||
@EnableLock
|
||||
@EnableRedisConfig
|
||||
public class SpringConfig {
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package com.github.houbb.lock.test.core;
|
||||
|
||||
import com.github.houbb.lock.api.core.ILock;
|
||||
import com.github.houbb.lock.core.core.LockSpinRe;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class LockSpinReThread implements Runnable {
|
||||
|
||||
private final ILock lock = new LockSpinRe();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println("first-lock: " + Thread.currentThread().getId());
|
||||
lock.lock();
|
||||
|
||||
System.out.println("second-lock: " + Thread.currentThread().getId());
|
||||
lock.lock();
|
||||
lock.unlock();
|
||||
System.out.println("second-unlock: " + Thread.currentThread().getId());
|
||||
|
||||
lock.unlock();
|
||||
System.out.println("first-unlock: " + Thread.currentThread().getId());
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
final Runnable runnable = new LockSpinReThread();
|
||||
new Thread(runnable).start();
|
||||
new Thread(runnable).start();
|
||||
new Thread(runnable).start();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package com.github.houbb.lock.test.core;
|
||||
|
||||
import com.github.houbb.lock.api.core.ILock;
|
||||
import com.github.houbb.lock.core.core.LockSpin;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class LockSpinThread implements Runnable {
|
||||
|
||||
private final ILock lock = new LockSpin();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println("first-lock: " + Thread.currentThread().getId());
|
||||
lock.lock();
|
||||
|
||||
System.out.println("second-lock: " + Thread.currentThread().getId());
|
||||
lock.lock();
|
||||
lock.unlock();
|
||||
System.out.println("second-unlock: " + Thread.currentThread().getId());
|
||||
|
||||
lock.unlock();
|
||||
System.out.println("first-unlock: " + Thread.currentThread().getId());
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
final Runnable runnable = new LockSpinThread();
|
||||
new Thread(runnable).start();
|
||||
new Thread(runnable).start();
|
||||
new Thread(runnable).start();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package com.github.houbb.lock.test.core;
|
||||
|
||||
import com.github.houbb.heaven.util.util.DateUtil;
|
||||
import com.github.houbb.lock.api.core.ILock;
|
||||
import com.github.houbb.lock.core.core.LockWaitNotify;
|
||||
import com.github.houbb.log.integration.core.Log;
|
||||
import com.github.houbb.log.integration.core.LogFactory;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class LockWaitNotifyThread implements Runnable {
|
||||
|
||||
private static final Log log = LogFactory.getLog(LockWaitNotifyThread.class);
|
||||
|
||||
private final ILock lock = new LockWaitNotify();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
log.debug("first lock");
|
||||
|
||||
lock.lock();
|
||||
log.info("执行业务逻辑。");
|
||||
DateUtil.sleep(TimeUnit.SECONDS, 5);
|
||||
lock.unlock();
|
||||
log.debug("first unlock");
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
final Runnable runnable = new LockWaitNotifyThread();
|
||||
new Thread(runnable).start();
|
||||
new Thread(runnable).start();
|
||||
new Thread(runnable).start();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package com.github.houbb.lock.test.core;
|
||||
|
||||
import com.github.houbb.heaven.util.util.DateUtil;
|
||||
import com.github.houbb.lock.api.core.ILock;
|
||||
import com.github.houbb.lock.core.core.LockWaitNotify;
|
||||
import com.github.houbb.log.integration.core.Log;
|
||||
import com.github.houbb.log.integration.core.LogFactory;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class LockWaitNotifyThread2 implements Runnable {
|
||||
|
||||
private static final Log log = LogFactory.getLog(LockWaitNotifyThread2.class);
|
||||
|
||||
private final ILock lock = new LockWaitNotify();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
log.debug("first lock");
|
||||
|
||||
lock.lock();
|
||||
|
||||
log.debug("second lock");
|
||||
lock.lock();
|
||||
log.info("执行业务逻辑。");
|
||||
DateUtil.sleep(TimeUnit.SECONDS, 5);
|
||||
log.debug("second unlock");
|
||||
lock.unlock();
|
||||
|
||||
lock.unlock();
|
||||
log.debug("first unlock");
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
final Runnable runnable = new LockWaitNotifyThread2();
|
||||
new Thread(runnable).start();
|
||||
new Thread(runnable).start();
|
||||
new Thread(runnable).start();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package com.github.houbb.lock.test.core;
|
||||
|
||||
import com.github.houbb.lock.api.core.ILock;
|
||||
import com.github.houbb.lock.core.core.LockWaitNotifyRe;
|
||||
import com.github.houbb.log.integration.core.Log;
|
||||
import com.github.houbb.log.integration.core.LogFactory;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class LockWaitNotifyThreadRe implements Runnable {
|
||||
|
||||
private static final Log log = LogFactory.getLog(LockWaitNotifyThreadRe.class);
|
||||
|
||||
private final ILock lock = new LockWaitNotifyRe();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
log.debug("first lock");
|
||||
lock.lock();
|
||||
|
||||
log.debug("second lock");
|
||||
lock.lock();
|
||||
log.debug("second unlock");
|
||||
lock.unlock();
|
||||
|
||||
log.debug("first unlock");
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
final Runnable runnable = new LockWaitNotifyThreadRe();
|
||||
new Thread(runnable).start();
|
||||
new Thread(runnable).start();
|
||||
new Thread(runnable).start();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.github.houbb.lock.test.model;
|
||||
|
||||
public class User {
|
||||
|
||||
private String name;
|
||||
|
||||
private String id;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "User{" +
|
||||
"name='" + name + '\'' +
|
||||
", id='" + id + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.github.houbb.lock.test.service;
|
||||
|
||||
import com.github.houbb.lock.spring.annotation.Lock;
|
||||
import com.github.houbb.lock.test.model.User;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Service
|
||||
public class UserService {
|
||||
|
||||
public String rawUserName(Long userId) {
|
||||
return userId+"-name";
|
||||
}
|
||||
|
||||
@Lock
|
||||
public String queryUserName(Long userId) {
|
||||
return userId+"-name";
|
||||
}
|
||||
|
||||
@Lock(value = "#user.name")
|
||||
public void queryUserName2(User user) {
|
||||
System.out.println("user: " + user.toString());
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.github.houbb.lock.test;
|
||||
|
||||
import java.util.concurrent.Phaser;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class MyPhaser extends Phaser {
|
||||
|
||||
@Override
|
||||
protected boolean onAdvance(int phase, int registeredParties) {
|
||||
switch (phase) {
|
||||
case 0 :
|
||||
System.out.println("上半场完成");
|
||||
return false;
|
||||
case 1:
|
||||
System.out.println("下半场完成");
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package com.github.houbb.lock.test;
|
||||
|
||||
import java.util.concurrent.Phaser;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class PhaserDemo {
|
||||
|
||||
private static class GameRunnable implements Runnable {
|
||||
|
||||
private final Phaser phaser;
|
||||
|
||||
private GameRunnable(Phaser phaser) {
|
||||
this.phaser = phaser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
//参加上半场比赛
|
||||
System.out.println("玩家-"+Thread.currentThread().getName()+":参加上半场比赛");
|
||||
//执行这个方法的话会等所有的选手都完成了之后再继续下面的方法
|
||||
phaser.arriveAndAwaitAdvance();
|
||||
|
||||
|
||||
// 下半场
|
||||
//参加上半场比赛
|
||||
System.out.println("玩家-"+Thread.currentThread().getName()+":参加下半场比赛");
|
||||
//执行这个方法的话会等所有的选手都完成了之后再继续下面的方法
|
||||
phaser.arriveAndAwaitAdvance();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
int nums = 3;
|
||||
Phaser phaser = new MyPhaser();
|
||||
|
||||
//注册一次表示 phaser 维护的线程个数
|
||||
phaser.register();
|
||||
|
||||
for(int i = 0; i < nums; i++) {
|
||||
phaser.register();
|
||||
|
||||
Thread thread = new Thread(new GameRunnable(phaser));
|
||||
thread.start();
|
||||
}
|
||||
|
||||
//后续阶段主线程就不参加了
|
||||
phaser.arriveAndDeregister();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.github.houbb.lock.test.core;
|
||||
|
||||
|
||||
import com.github.houbb.id.core.core.Ids;
|
||||
import com.github.houbb.lock.api.core.ILock;
|
||||
import com.github.houbb.lock.core.bs.LockBs;
|
||||
import com.github.houbb.lock.core.support.format.LockKeyFormat;
|
||||
import com.github.houbb.lock.core.support.handler.LockReleaseFailHandler;
|
||||
import com.github.houbb.lock.core.support.lock.RedisLockSupport;
|
||||
import com.github.houbb.redis.config.core.factory.JedisRedisServiceFactory;
|
||||
import org.junit.Test;
|
||||
|
||||
public class LockBsTest {
|
||||
|
||||
@Test
|
||||
public void helloTest() {
|
||||
ILock lock = LockBs.newInstance();
|
||||
String key = "ddd";
|
||||
try {
|
||||
// 加锁
|
||||
lock.tryLock(key);
|
||||
System.out.println("业务处理");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
// 释放锁
|
||||
lock.unlock(key);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configTest() {
|
||||
LockBs.newInstance()
|
||||
.id(Ids.uuid32()) //id 生成策略
|
||||
.cache(JedisRedisServiceFactory.pooled("127.0.0.1", 6379)) //缓存策略
|
||||
.lockSupport(new RedisLockSupport()) // 锁实现策略
|
||||
.lockKeyFormat(new LockKeyFormat()) // 针对 key 的格式化处理策略
|
||||
.lockReleaseFailHandler(new LockReleaseFailHandler()) //释放锁失败处理
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package com.github.houbb.lock.test.lock;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class ArrayBlockingQueueDemo {
|
||||
|
||||
private ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
|
||||
|
||||
public void put(final String put) throws InterruptedException {
|
||||
System.out.println("设置开始");
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
queue.put(put);
|
||||
System.out.println("设置完成: " + put);
|
||||
}
|
||||
|
||||
public void take() throws InterruptedException {
|
||||
System.out.println("获取开始");
|
||||
String take = queue.take();
|
||||
System.out.println("获取成功: " + take);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
final ArrayBlockingQueueDemo queueTest = new ArrayBlockingQueueDemo();
|
||||
// 写入线程
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
for(int i = 0; i < 3; i++) {
|
||||
queueTest.put(i+"T");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
||||
// 读取线程
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (true) {
|
||||
queueTest.take();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package com.github.houbb.lock.test.lock;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class ConcurrentLinkedQueueTest {
|
||||
|
||||
@Test
|
||||
public void helloTest() {
|
||||
ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
// add() 将指定元素插入此队列的尾部。
|
||||
queue.add("add");
|
||||
|
||||
// offer() 将指定元素插入此队列的尾部。
|
||||
queue.offer("offer");
|
||||
|
||||
// peek() 获取但不移除此队列的头;如果此队列为空,则返回 null
|
||||
String value = queue.peek();
|
||||
System.out.println("PEEK: " + value);
|
||||
|
||||
// poll() 获取并移除此队列的头,如果此队列为空,则返回 null。
|
||||
String poll = queue.poll();
|
||||
System.out.println("POLL: " + poll);
|
||||
|
||||
// remove() 移除 从队列中移除指定元素的单个实例(如果存在)。
|
||||
boolean remove = queue.remove("offer");
|
||||
System.out.println("Remove result: " + remove);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package com.github.houbb.lock.test.lock;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class CowArraySetDemo {
|
||||
|
||||
/**
|
||||
* 读线程
|
||||
*/
|
||||
private static class ReadTask implements Runnable {
|
||||
Set<String> set;
|
||||
|
||||
public ReadTask(Set<String> set) {
|
||||
this.set = set;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
System.out.println(set);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 写线程
|
||||
*/
|
||||
private static class WriteTask implements Runnable {
|
||||
private Set<String> set;
|
||||
private String value;
|
||||
|
||||
public WriteTask(Set<String> set, String value) {
|
||||
this.set = set;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
set.remove(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
final int NUM = 5;
|
||||
Set<String> set = new CopyOnWriteArraySet<>();
|
||||
for (int i = 0; i < NUM; i++) {
|
||||
set.add("main_" + i);
|
||||
}
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(NUM);
|
||||
for (int i = 0; i < NUM; i++) {
|
||||
executorService.execute(new WriteTask(set, "main_" + i));
|
||||
executorService.execute(new ReadTask(set));
|
||||
}
|
||||
executorService.shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
package com.github.houbb.lock.test.lock;
|
||||
|
||||
import java.util.concurrent.DelayQueue;
|
||||
import java.util.concurrent.Delayed;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @see java.util.concurrent.DelayQueue
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class DelayQueueDemo {
|
||||
|
||||
|
||||
/**
|
||||
* 写入线程
|
||||
* @author 老马啸西风
|
||||
*/
|
||||
private static class WriteThread extends Thread {
|
||||
|
||||
private final DelayQueue<DelayElem> delayQueue;
|
||||
|
||||
private WriteThread(DelayQueue<DelayElem> delayQueue) {
|
||||
this.delayQueue = delayQueue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for(int i = 0; i < 3; i++) {
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
DelayElem element = new DelayElem(1000,i+"test");
|
||||
delayQueue.offer(element);
|
||||
System.out.println(System.currentTimeMillis() + " 放入元素 " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取线程
|
||||
* @author 老马啸西风
|
||||
*/
|
||||
private static class ReadThread extends Thread {
|
||||
|
||||
private final DelayQueue<DelayElem> delayQueue;
|
||||
|
||||
private ReadThread(DelayQueue<DelayElem> delayQueue) {
|
||||
this.delayQueue = delayQueue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true){
|
||||
try {
|
||||
DelayElem element = delayQueue.take();
|
||||
System.out.println(System.currentTimeMillis() +" 获取元素:" + element);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class DelayElem implements Delayed {
|
||||
|
||||
/**
|
||||
* 延迟时间
|
||||
*/
|
||||
private final long delay;
|
||||
/**
|
||||
* 到期时间
|
||||
*/
|
||||
private final long expire;
|
||||
/**
|
||||
* 数据
|
||||
*/
|
||||
private final String msg;
|
||||
|
||||
private DelayElem(long delay, String msg) {
|
||||
this.delay = delay;
|
||||
this.msg = msg;
|
||||
//到期时间 = 当前时间+延迟时间
|
||||
this.expire = System.currentTimeMillis() + this.delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* 需要实现的接口,获得延迟时间
|
||||
*
|
||||
* 用过期时间-当前时间
|
||||
* @param unit 时间单位
|
||||
* @return 延迟时间
|
||||
*/
|
||||
@Override
|
||||
public long getDelay(TimeUnit unit) {
|
||||
return unit.convert(this.expire - System.currentTimeMillis() , TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于延迟队列内部比较排序
|
||||
* <p>
|
||||
* 当前时间的延迟时间 - 比较对象的延迟时间
|
||||
*
|
||||
* @param o 比较对象
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(Delayed o) {
|
||||
return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DelayElem{" +
|
||||
"delay=" + delay +
|
||||
", expire=" + expire +
|
||||
", msg='" + msg + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
DelayQueue<DelayElem> delayQueue = new DelayQueue<>();
|
||||
|
||||
new WriteThread(delayQueue).start();
|
||||
new ReadThread(delayQueue).start();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package com.github.houbb.lock.test.lock;
|
||||
|
||||
import java.util.concurrent.BlockingDeque;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class LinkedBlockingDequeDemo {
|
||||
|
||||
private static class Producer implements Runnable{
|
||||
private BlockingDeque<Integer> queue;
|
||||
public Producer(BlockingDeque<Integer> queue) {
|
||||
this.queue = queue;
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
while(true) {
|
||||
try {
|
||||
Integer num = ThreadLocalRandom.current().nextInt(100);
|
||||
queue.put(num);
|
||||
System.out.println(String.format("%s producer a num %d",Thread.currentThread().getName(),num));
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class Consumer implements Runnable{
|
||||
private BlockingDeque<Integer> queue;
|
||||
public Consumer(BlockingDeque<Integer> queue) {
|
||||
this.queue = queue;
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
while(true) {
|
||||
try {
|
||||
System.out.println(String.format("%s consume a num %d",Thread.currentThread().getName(),queue.take()));
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
BlockingDeque<Integer> queue = new LinkedBlockingDeque<>(100);
|
||||
new Thread(new Producer(queue),"Producer").start();
|
||||
new Thread(new Consumer(queue),"Consumer").start();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package com.github.houbb.lock.test.lock;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class LinkedBlockingQueueDemo {
|
||||
|
||||
private BlockingQueue<String> queue = new LinkedBlockingQueue<>(3);
|
||||
|
||||
public void put(final String put) throws InterruptedException {
|
||||
System.out.println("设置开始");
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
queue.put(put);
|
||||
System.out.println("设置完成: " + put);
|
||||
}
|
||||
|
||||
public void take() throws InterruptedException {
|
||||
System.out.println("获取开始");
|
||||
String take = queue.take();
|
||||
System.out.println("获取成功: " + take);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
final LinkedBlockingQueueDemo queueTest = new LinkedBlockingQueueDemo();
|
||||
// 写入线程
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
for(int i = 0; i < 3; i++) {
|
||||
queueTest.put(i+"T");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
||||
// 读取线程
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (true) {
|
||||
queueTest.take();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
package com.github.houbb.lock.test.lock;
|
||||
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class PriorityBlockingQueueDemo {
|
||||
|
||||
private static class User implements Comparable<User> {
|
||||
|
||||
private final int order;
|
||||
|
||||
private final String name;
|
||||
|
||||
private User(int order, String name) {
|
||||
this.order = order;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(User o) {
|
||||
return this.order - o.order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "User{" +
|
||||
"order=" + order +
|
||||
", name='" + name + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
private PriorityBlockingQueue<User> queue = new PriorityBlockingQueue<>();
|
||||
|
||||
public void put(final User user) throws InterruptedException {
|
||||
System.out.println("设置开始");
|
||||
queue.put(user);
|
||||
System.out.println("设置完成: " + user);
|
||||
}
|
||||
|
||||
public void take() throws InterruptedException {
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
System.out.println("获取开始");
|
||||
User take = queue.take();
|
||||
System.out.println("获取成功: " + take);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
final PriorityBlockingQueueDemo queueTest = new PriorityBlockingQueueDemo();
|
||||
// 写入线程
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
for(int i = 0; i < 5; i++) {
|
||||
int order = ThreadLocalRandom.current().nextInt(10);
|
||||
User user = new User(order, i+"-user");
|
||||
queueTest.put(user);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
||||
// 读取线程
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (true) {
|
||||
queueTest.take();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package com.github.houbb.lock.test.lock;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
* @see java.util.concurrent.SynchronousQueue
|
||||
*/
|
||||
public class SynchronousQueueDemo {
|
||||
}
|
||||
@@ -1,37 +1,37 @@
|
||||
package com.github.houbb.lock.test.redis;
|
||||
|
||||
import com.github.houbb.lock.api.core.ILock;
|
||||
import com.github.houbb.lock.api.support.IOperator;
|
||||
import com.github.houbb.lock.core.bs.LockBs;
|
||||
import com.github.houbb.lock.redis.support.operator.JedisOperator;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import redis.clients.jedis.Jedis;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 0.0.1
|
||||
*/
|
||||
@Ignore
|
||||
public class LockRedisTest {
|
||||
|
||||
@Test
|
||||
public void helloTest() {
|
||||
Jedis jedis = new Jedis("127.0.0.1", 6379);
|
||||
IOperator operator = new JedisOperator(jedis);
|
||||
|
||||
// 获取锁
|
||||
ILock lock = LockBs.newInstance(operator).lock();
|
||||
|
||||
try {
|
||||
boolean lockResult = lock.tryLock();
|
||||
System.out.println(lockResult);
|
||||
// 业务处理
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//package com.github.houbb.lock.test.redis;
|
||||
//
|
||||
//import com.github.houbb.lock.api.core.ILock;
|
||||
//import com.github.houbb.lock.api.support.IOperator;
|
||||
//import com.github.houbb.lock.core.bs.LockBs;
|
||||
//import com.github.houbb.lock.redis.support.operator.JedisOperator;
|
||||
//import org.junit.Ignore;
|
||||
//import org.junit.Test;
|
||||
//import redis.clients.jedis.Jedis;
|
||||
//
|
||||
///**
|
||||
// * @author binbin.hou
|
||||
// * @since 0.0.1
|
||||
// */
|
||||
//@Ignore
|
||||
//public class LockRedisTest {
|
||||
//
|
||||
// @Test
|
||||
// public void helloTest() {
|
||||
// Jedis jedis = new Jedis("127.0.0.1", 6379);
|
||||
// IOperator operator = new JedisOperator(jedis);
|
||||
//
|
||||
// // 获取锁
|
||||
// ILock lock = LockBs.newInstance(operator).lock();
|
||||
//
|
||||
// try {
|
||||
// boolean lockResult = lock.tryLock();
|
||||
// System.out.println(lockResult);
|
||||
// // 业务处理
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// } finally {
|
||||
// lock.unlock();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.github.houbb.lock.test.spring;
|
||||
|
||||
|
||||
import com.github.houbb.lock.core.bs.LockBs;
|
||||
import com.github.houbb.lock.test.config.SpringConfig;
|
||||
import com.github.houbb.lock.test.model.User;
|
||||
import com.github.houbb.lock.test.service.UserService;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@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 {
|
||||
lockBs.tryLock(key);
|
||||
final String value = userService.rawUserName(1L);
|
||||
} catch (Exception exception) {
|
||||
throw new RuntimeException(exception);
|
||||
} finally {
|
||||
lockBs.unlock(key);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.github.houbb.lock.test.spring;
|
||||
|
||||
|
||||
import com.github.houbb.lock.test.config.SpringConfig;
|
||||
import com.github.houbb.lock.test.model.User;
|
||||
import com.github.houbb.lock.test.service.UserService;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@ContextConfiguration(classes = SpringConfig.class)
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class SpringServiceTest {
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Test
|
||||
public void queryLogTest() {
|
||||
final String key = "name";
|
||||
final String value = userService.queryUserName(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryUserNameTest() {
|
||||
User user = new User();
|
||||
user.setId("001");
|
||||
user.setName("u-001");
|
||||
userService.queryUserName2(user);
|
||||
}
|
||||
|
||||
}
|
||||
23
lock-test/src/test/resources/logback.xml
Normal file
23
lock-test/src/test/resources/logback.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration debug="false">
|
||||
|
||||
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径 /app/sv/logs -->
|
||||
<!-- 控制台输出 -->
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
|
||||
<charset>utf-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!--com.github.houbb 包路径 additivity: 表示是否 想上层传递)-->
|
||||
<logger name="com.github.houbb.lock" level="INFO" additivity="true">
|
||||
</logger>
|
||||
|
||||
<!-- 日志输出级别-->
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
47
lock-test2/pom.xml
Normal file
47
lock-test2/pom.xml
Normal file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>lock</artifactId>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<version>1.3.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>lock-test2</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>lock-springboot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>test-spring</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>slf4j-common</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<version>1.1.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.1.11</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* @author d
|
||||
* @since 1.0.0
|
||||
*/
|
||||
package com.github.houbb.lock.test2;
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.github.houbb.lock.test2.service;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 0.0.3
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class MyApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MyApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.github.houbb.lock.test2.service;
|
||||
|
||||
import com.github.houbb.lock.spring.annotation.Lock;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 0.0.1
|
||||
*/
|
||||
@Service
|
||||
public class UserService {
|
||||
|
||||
@Lock
|
||||
public void queryInfo(final String id) {
|
||||
System.out.println("query info: " + id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.github.houbb.lock.test2.service;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author binbin.hou
|
||||
* @since 0.0.2
|
||||
*/
|
||||
@ContextConfiguration(classes = MyApplication.class)
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class UserServiceTest {
|
||||
|
||||
@Autowired
|
||||
private UserService service;
|
||||
|
||||
@Test
|
||||
public void queryTest() {
|
||||
service.queryInfo("1");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* @author d
|
||||
* @since 1.0.0
|
||||
*/
|
||||
package com.github.houbb.lock.test2.service;
|
||||
78
pom.xml
78
pom.xml
@@ -5,12 +5,14 @@
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>lock</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>0.0.4</version>
|
||||
<version>1.3.0</version>
|
||||
<modules>
|
||||
<module>lock-api</module>
|
||||
<module>lock-core</module>
|
||||
<module>lock-test</module>
|
||||
<module>lock-redis</module>
|
||||
<module>lock-spring</module>
|
||||
<module>lock-springboot-starter</module>
|
||||
<module>lock-test2</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
@@ -34,14 +36,19 @@
|
||||
|
||||
<!--============================== INTER ==============================-->
|
||||
<plugin.gen.version>1.0.6</plugin.gen.version>
|
||||
<heaven.version>0.1.161</heaven.version>
|
||||
|
||||
<heaven.version>0.1.167</heaven.version>
|
||||
<id.version>0.0.6</id.version>
|
||||
<wait.version>0.0.1</wait.version>
|
||||
<log-integration.version>1.1.8</log-integration.version>
|
||||
<common-cache.version>0.0.5</common-cache.version>
|
||||
<redis-config.version>1.4.0</redis-config.version>
|
||||
<aop.version>0.0.2</aop.version>
|
||||
<test.version>0.0.1</test.version>
|
||||
|
||||
<!--============================== OTHER ==============================-->
|
||||
<junit.version>4.12</junit.version>
|
||||
<jedis.version>2.8.1</jedis.version>
|
||||
<spring-boot.version>1.5.22.RELEASE</spring-boot.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
@@ -59,7 +66,12 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>lock-redis</artifactId>
|
||||
<artifactId>lock-spring</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>lock-springboot-starter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
@@ -69,6 +81,11 @@
|
||||
<artifactId>heaven</artifactId>
|
||||
<version>${heaven.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>id-api</artifactId>
|
||||
<version>${id.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>id-core</artifactId>
|
||||
@@ -85,6 +102,33 @@
|
||||
<version>${log-integration.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>common-cache-api</artifactId>
|
||||
<version>${common-cache.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>common-cache-core</artifactId>
|
||||
<version>${common-cache.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>redis-config-core</artifactId>
|
||||
<version>${redis-config.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>redis-config-spring</artifactId>
|
||||
<version>${redis-config.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>redis-config-springboot-starter</artifactId>
|
||||
<version>${redis-config.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!--============================== OTHER ==============================-->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
@@ -95,10 +139,28 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
<version>${jedis.version}</version>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>aop-core</artifactId>
|
||||
<version>${aop.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>aop-spring</artifactId>
|
||||
<version>${aop.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>test-spring</artifactId>
|
||||
<version>${test.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@ ECHO "============================= RELEASE START..."
|
||||
|
||||
:: 版本号信息(需要手动指定)
|
||||
:::: 旧版本名称
|
||||
SET version=0.0.4
|
||||
SET version=1.3.0
|
||||
:::: 新版本名称
|
||||
SET newVersion=0.0.5
|
||||
SET newVersion=1.4.0
|
||||
:::: 组织名称
|
||||
SET groupName=com.github.houbb
|
||||
:::: 项目名称
|
||||
|
||||
Reference in New Issue
Block a user