com.xzchaoo.commons.syncexclusiveexecutor.SyncExclusiveExecutorImpl Maven / Gradle / Ivy
The newest version!
package com.xzchaoo.commons.syncexclusiveexecutor;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import com.xzchaoo.commons.basic.Ack;
/**
* created at 2020/7/25
*
* @author xzchaoo
*/
public class SyncExclusiveExecutorImpl implements SyncExclusiveExecutor {
private static final int NO_TYPE = -1;
/**
* Lock
*/
private final Lock lock = new ReentrantLock();
/**
* Wait map for every type
*/
private final Map waitMap = new HashMap<>();
/**
* Limiter
*/
private final Limiter limiter;
/**
* Executing type
*/
private int executingType = NO_TYPE;
/**
* Max executed count before give up.
*/
private int maxExecuted;
/**
* Max work-in-progress count
*/
private int maxWip;
/**
* Release time to give up.
*/
private long forceReleaseTime;
/**
* Executed count
*/
private int executed;
/**
* Work in process count
*/
private int wip;
/**
* Indicate whether other types has waiting
*/
private boolean otherHasWaiting;
private int waiting;
public SyncExclusiveExecutorImpl(int maxExecuted, int maxWip, int maxTime) {
this.limiter = new Limiter.Fixed(maxExecuted, maxWip, maxTime);
}
public SyncExclusiveExecutorImpl(Limiter limiter) {
this.limiter = limiter;
}
@Override
public Ack acquire(int type) {
if (type < 0) {
throw new IllegalStateException("type < 0");
}
lock.lock();
try {
++waiting;
Wait wait = waitMap.computeIfAbsent(type, Wait::new);
for (; ; ) {
if (executingType == NO_TYPE) {
Limit limit = limiter.limit(type);
this.executingType = type;
this.maxExecuted = limit.getMaxExecuted();
this.maxWip = limit.getMaxWip();
this.forceReleaseTime = limit.getMaxTime() > 0 ? (
System.currentTimeMillis() + limit.getMaxTime()) : 0;
++executed;
++wip;
break;
} else if (executingType == type) {
if (!otherHasWaiting) {
++executed;
++wip;
break;
} else {
long now = System.currentTimeMillis();
// 20210430: 如果缺少 executed==0 的or条件, 那么可能会走进else分支
// 从而导致死锁, 这里加一个 executed == 0 条件可以让至少1个线程走进这个if分支, 从而不死锁
if (executed == 0 || executed < maxExecuted && (maxWip <= 0
|| wip < maxWip) && (forceReleaseTime <= 0
|| now < forceReleaseTime)) {
++executed;
++wip;
break;
} else {
if (wip == 0) {
switchToOther();
} else {
wait.await();
}
}
}
} else {
otherHasWaiting = true;
wait.await();
}
}
} finally {
--waiting;
lock.unlock();
}
return Ack.once(this::ack);
}
@Override
public Stat stat() {
Stat stat = new Stat();
stat.setWaiting(waiting);
stat.setWip(wip);
stat.setExecutingType(executingType);
stat.setExecuted(executed);
stat.setForceReleaseTime(forceReleaseTime);
return stat;
}
private void ack() {
lock.lock();
try {
--wip;
Wait wait = waitMap.get(executingType);
// 如果当前类型有任务, 并且可以继续执行, 那么就signal一个等待着
// 可以继续执行的条件是:
// 1. 没有其他类型的任务正在等待
// 2. 有其他任务正在等待, 但自己还可以继续执行
if (wait.waiting > 0 && (!otherHasWaiting || (executed < maxExecuted
&& (forceReleaseTime <= 0
|| System.currentTimeMillis() < forceReleaseTime)))) {
wait.condition.signal();
// 否则当前类型已经达到要放弃执行的阶段了, 待wip达到0就切换到其他type
} else if (wip == 0) {
// 如果wip==0, 那么一定要记得清空状态, 否则下一次在添加任务, 那个任务无法执行!
switchToOther();
}
} finally {
lock.unlock();
}
}
private void switchToOther() {
int currentType = this.executingType;
this.executed = 0;
this.executingType = NO_TYPE;
this.otherHasWaiting = false;
this.forceReleaseTime = 0;
for (Map.Entry e : waitMap.entrySet()) {
if (e.getKey() != currentType && e.getValue().waiting > 0) {
for (Map.Entry e2 : waitMap.entrySet()) {
if (!e.getKey().equals(e2.getKey())
&& e2.getValue().waiting > 0) {
otherHasWaiting = true;
break;
}
}
Limit limit = limiter.limit(e.getKey());
this.maxExecuted = limit.getMaxExecuted();
this.maxWip = limit.getMaxWip();
this.executingType = e.getKey();
this.forceReleaseTime = limit.getMaxTime() > 0 ? (
System.currentTimeMillis() + limit.getMaxTime()) : 0;
e.getValue().condition.signalAll();
break;
}
}
// 假设maxTime是1000ms, 这里sleep 1200ms, 并且去掉 executed==0 的判断就会导致死锁(走进else分支)
// SleepUtils.sleepSilently(1200);
}
private class Wait {
final int type;
final Condition condition = lock.newCondition();
int waiting;
Wait(int type) {
this.type = type;
}
void await() {
waiting++;
try {
condition.await();
} catch (InterruptedException e) {
throw new IllegalStateException("await interrupted", e);
} finally {
waiting--;
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy