Skip to content

Commit 3961c7f

Browse files
committed
version 1.0.0.0 update
修复时长溢出的BUG;完善乐观锁,现在可以使用自定义的乐观锁,并指定最长等待时间、最长执行时间、是否强制执行等,全方位避免死锁
1 parent 34d225d commit 3961c7f

10 files changed

Lines changed: 243 additions & 57 deletions

File tree

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,7 @@
7979
<tr>
8080
<td>0.9.0.0</td><td>完善读写锁注解,现在可以使用自定义的读写锁</td><td>2022年2月4日</td>
8181
</tr>
82+
<tr>
83+
<td>1.0.0.0</td><td>修复时长溢出的BUG;完善乐观锁,现在可以使用自定义的乐观锁,并指定最长等待时间、最长执行时间、是否强制执行等,全方位避免死锁</td><td>2022年2月5日</td>
84+
</tr>
8285
</table>

src/main/java/org/springframework/lock/annotation/OptimisticLock.java

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,39 @@
1111
@Documented
1212
public @interface OptimisticLock {
1313

14+
/**
15+
* 乐观锁变量的名称
16+
* @return 默认使用自动生成的锁
17+
*/
18+
String value() default "";
19+
1420
/**
1521
* 乐观锁忙等待周期毫秒数
1622
* @return 乐观锁忙等待时间
1723
*/
18-
long value() default 500L;
24+
long pollingTime() default 500L;
25+
26+
/**
27+
* 最长等待时间
28+
* @return 默认Long.MAX_VALUE
29+
*/
30+
long waitTime() default Long.MAX_VALUE;
31+
32+
/**
33+
* 最长执行时间,超过此时间自动解锁
34+
* @return 默认Long.MAX_VALUE
35+
*/
36+
long executeTime() default Long.MAX_VALUE;
37+
38+
/**
39+
* 是否强制执行,如果等待时间耗尽还没有拿到锁的话,是否还要执行
40+
* @return 默认放弃
41+
*/
42+
boolean isContinueIfElapsed() default false;
43+
44+
/**
45+
* 如果强制执行,是否中断其他线程以强制获得锁. 该属性仅在{@code isContinueIfElapsed}为{@code true}时才生效
46+
* @return 默认否
47+
*/
48+
boolean withLockIfContinue() default false;
1949
}

src/main/java/org/springframework/lock/annotation/ReadLock.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
boolean isContinueIfElapsed() default false;
4646

4747
/**
48-
* 如果强制执行,是否中断其他线程以强制获得锁
48+
* 如果强制执行,是否中断其他线程以强制获得锁.该属性仅在{@code isContinueIfElapsed}为{@code true}时才生效
4949
* @return 默认否
5050
*/
5151
boolean withLockIfContinue() default false;

src/main/java/org/springframework/lock/annotation/WriteLock.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
boolean isContinueIfElapsed() default false;
4646

4747
/**
48-
* 如果强制执行,是否中断其他线程以强制获得锁
48+
* 如果强制执行,是否中断其他线程以强制获得锁. 该属性仅在{@code isContinueIfElapsed}为{@code true}时才生效
4949
* @return 默认否
5050
*/
5151
boolean withLockIfContinue() default false;

src/main/java/org/springframework/lock/aspect/OptimisticLockAspect.java

Lines changed: 132 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@
77
import org.aspectj.lang.annotation.Aspect;
88
import org.aspectj.lang.reflect.MethodSignature;
99
import org.springframework.lock.annotation.OptimisticLock;
10+
import org.springframework.lock.timer.InterruptTimer;
11+
import org.springframework.util.StringUtils;
1012

1113
import java.lang.reflect.Field;
1214
import java.lang.reflect.Method;
1315
import java.util.concurrent.atomic.AtomicBoolean;
16+
import java.util.concurrent.atomic.AtomicLong;
1417

1518
import static java.lang.Thread.sleep;
19+
import static java.util.concurrent.TimeUnit.MILLISECONDS;
1620

1721
/**
1822
* 乐观锁的切面
@@ -33,48 +37,143 @@ public class OptimisticLockAspect {
3337
*/
3438
@Around("@annotation(org.springframework.lock.annotation.OptimisticLock)")
3539
public Object aroundOptimisticLock(ProceedingJoinPoint jp) throws Throwable {
36-
Object obj = jp.getTarget();
37-
Class<?> clz = obj.getClass();
38-
// 获取等待时长,默认500毫秒
39-
long waitTime = 500L;
40+
// 获取注解属性值
41+
long pollingTime = 500L;
42+
long waitTime = Long.MAX_VALUE;
43+
long executeTime = Long.MAX_VALUE;
44+
boolean isContinueIfElapsed = false;
45+
boolean withLockIfContinue = false;
46+
String lockName = null;
47+
4048
MethodSignature signature = (MethodSignature) jp.getSignature();
4149
Method method = signature.getMethod();
42-
if (method != null) {
43-
OptimisticLock annotation = method.getAnnotation(OptimisticLock.class);
44-
if (annotation != null) {
45-
waitTime = annotation.value();
46-
}
50+
OptimisticLock annotation = method.getAnnotation(OptimisticLock.class);
51+
if (annotation != null) {
52+
pollingTime = annotation.pollingTime();
53+
waitTime = annotation.waitTime();
54+
executeTime = annotation.executeTime();
55+
isContinueIfElapsed = annotation.isContinueIfElapsed();
56+
withLockIfContinue = annotation.withLockIfContinue();
57+
lockName = annotation.value();
4758
}
59+
4860
// 获取锁对象
49-
AtomicBoolean lock = null;
50-
for (Field field : clz.getDeclaredFields()) {
51-
if ("$opLock".equals(field.getName())){
52-
field.setAccessible(true);
53-
Object unknownLock = field.get(obj);
54-
lock = (AtomicBoolean) unknownLock;
55-
}
61+
AtomicLong lock = null;
62+
Object obj = jp.getTarget();
63+
Class<?> clz = obj.getClass();
64+
Field field = null;
65+
66+
if (StringUtils.hasText(lockName)){
67+
field = clz.getDeclaredField(lockName);
68+
field.setAccessible(true);
69+
lock = (AtomicLong) field.get(obj);
70+
}else{
71+
field = clz.getDeclaredField("$opLock");
72+
field.setAccessible(true);
73+
lock = (AtomicLong) field.get(obj);
74+
}
75+
76+
if (lock == null){
77+
LOGGER.warn(clz.getSimpleName() + "获取锁错误,请检查锁名称是否正确");
78+
return jp.proceed();
5679
}
80+
81+
// 尝试加锁,不行就解掉别人的锁
5782
Object result = null;
58-
if (lock != null){
59-
while (true){
60-
if (lock.compareAndSet(true, false))
61-
// 拿到了锁
62-
break;
63-
else
64-
// 如果没拿到锁就忙等待
65-
sleep(waitTime);
83+
boolean locked = this.tryLock(lock, waitTime, pollingTime);
84+
if (locked){
85+
result = this.processMethod(jp, lock, executeTime);
86+
}else {
87+
if (isContinueIfElapsed){
88+
if (withLockIfContinue){
89+
Thread lockedThread = findThread(lock.get());
90+
if (lockedThread == null)
91+
result = this.processMethod(jp, lock, executeTime);
92+
else {
93+
lockedThread.interrupt();
94+
if (this.tryLock(lock, waitTime, pollingTime)){
95+
LOGGER.warn("等待时间耗尽,终止线程" + lockedThread + "以强制获得乐观锁@" + lock.hashCode());
96+
result = this.processMethod(jp, lock, executeTime);
97+
}else {
98+
lockedThread.stop();
99+
if (this.tryLock(lock, waitTime, pollingTime)){
100+
LOGGER.warn("等待时间耗尽,终止线程" + lockedThread + "以强制获得乐观锁@" + lock.hashCode());
101+
result = this.processMethod(jp, lock, executeTime);
102+
}
103+
}
104+
}
105+
}else {
106+
LOGGER.warn("等待时间耗尽,将不带锁执行" + method.getName());
107+
result = jp.proceed();
108+
}
109+
}else {
110+
LOGGER.warn("等待时间耗尽,放弃执行" + method.getName());
66111
}
67-
try {
68-
LOGGER.info(clz.getSimpleName() + "获得乐观锁");
69-
result = jp.proceed();
70-
LOGGER.info(clz.getSimpleName() + "释放乐观锁");
71-
}finally {
72-
lock.set(true);
73-
}
74-
}else{
75-
LOGGER.warn(clz.getSimpleName() + "生成乐观锁失败,未能加锁");
112+
}
113+
return result;
114+
}
115+
116+
/**
117+
* 处理方法
118+
* @param jp 切入点
119+
* @param lock 乐观锁,调用方法前必须加锁
120+
* @param executeTime 最长执行时间,超出此时间结束线程
121+
* @return 原方法返回值
122+
* @throws Throwable 异常
123+
*/
124+
private Object processMethod(ProceedingJoinPoint jp, AtomicLong lock, long executeTime) throws Throwable {
125+
Object result = null;
126+
LOGGER.info(Thread.currentThread().getName() + "获得乐观锁@" + lock.hashCode());
127+
if (System.currentTimeMillis() + executeTime > 0)
128+
new InterruptTimer(Thread.currentThread(), executeTime);
129+
try{
76130
result = jp.proceed();
131+
} finally {
132+
LOGGER.info(Thread.currentThread().getName() + "释放乐观锁@" + lock.hashCode());
133+
lock.set(0);
77134
}
78135
return result;
79136
}
137+
138+
/**
139+
* 通过线程id获取线程
140+
* @param threadId 线程id
141+
* @return 线程
142+
*/
143+
private static Thread findThread(long threadId) {
144+
ThreadGroup group = Thread.currentThread().getThreadGroup();
145+
while(group != null) {
146+
Thread[] threads = new Thread[(int)(group.activeCount() * 1.2)];
147+
int count = group.enumerate(threads, true);
148+
for(int i = 0; i < count; i++) {
149+
if(threadId == threads[i].getId()) {
150+
return threads[i];
151+
}
152+
}
153+
group = group.getParent();
154+
}
155+
return null;
156+
}
157+
158+
/**
159+
* 忙等待,直到超时或获取锁
160+
* @param lock 锁
161+
* @param waitTime 最长等待时长
162+
* @param pollingTime 轮询周期
163+
* @return 是否获得锁
164+
*/
165+
private boolean tryLock(AtomicLong lock, long waitTime, long pollingTime) throws InterruptedException {
166+
long startWaitTime = System.currentTimeMillis();
167+
while (true){
168+
if (lock.compareAndSet(0, Thread.currentThread().getId())) {
169+
// 拿到了锁
170+
return true;
171+
}else if (System.currentTimeMillis() - startWaitTime > waitTime)
172+
// 超时了
173+
return false;
174+
else
175+
// 如果没拿到锁就忙等待
176+
sleep(pollingTime);
177+
}
178+
}
80179
}

src/main/java/org/springframework/lock/aspect/ReadLockAspect.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ public Object aroundReadLock(ProceedingJoinPoint jp) throws Throwable {
126126
private Object processMethod(ProceedingJoinPoint jp, Lock readLock, long executeTime) throws Throwable {
127127
Object result = null;
128128
LOGGER.info(Thread.currentThread().getName() + "获得读锁" + readLock);
129-
new InterruptTimer(Thread.currentThread(), executeTime);
129+
if (System.currentTimeMillis() + executeTime > 0)
130+
new InterruptTimer(Thread.currentThread(), executeTime);
130131
try {
131132
result = jp.proceed();
132133
} finally {

src/main/java/org/springframework/lock/aspect/WriteLockAspect.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ public Object aroundWriteLock(ProceedingJoinPoint jp) throws Throwable {
127127
private Object processMethod(ProceedingJoinPoint jp, Lock writeLock, long executeTime) throws Throwable {
128128
Object result = null;
129129
LOGGER.info(Thread.currentThread().getName() + "获得写锁" + writeLock);
130-
new InterruptTimer(Thread.currentThread(), executeTime);
130+
if (System.currentTimeMillis() + executeTime > 0)
131+
new InterruptTimer(Thread.currentThread(), executeTime);
131132
try {
132133
result = jp.proceed();
133134
} finally {

0 commit comments

Comments
 (0)