在并发环境中,多个线程调用方法。在该方法未被调用一段时间(例如5秒)之后,应该调用空闲事件。
在没有调用方法的情况下再过5秒钟后,不应再次调用idle事件。应在第一次方法调用后再次启动空闲计时器。
public class Service {
public void do(Foo foo) {
// process foo
}
private void onIdle() {
}
}
实施此措施的安全有效方法是什么?
如果没有第二个要求(不要一次又一次地调用onIdle())可以做得更好,那么这是可以接受的。
答案 0 :(得分:1)
分开关注点。让服务保持关于被调用的状态,让另一个线程轮询空闲方法:
public class Service {
private volatile long lastActivity;
public void do(Foo foo) {
// process foo
lastActivity = System.currentTimeMillis();
}
private void onIdle() {
if (lastActivity > 0 && System.currentTimeMillis() - lastActivity > 5000) {
// do stuff
lastActivity = 0;
}
}
}
然后让任务在另一个线程中每隔5秒运行一次:
public void run() {
service.onIdle();
}
您可以使用开箱即用的环境(例如Spring的@Scheduled
)来安排任务,也可以在while (true)
Koop中调用onIdle()后创建自己的线程,该线程只需要休息5秒。
答案 1 :(得分:0)
如果你能负担得起专门的线程来观看你的服务,这是一个实现。
此代码的保证是,任何时候都不能有多个活动观察者;如果客户呼叫onCall
,那么在该呼叫之后总会有一个活着的观察者。
(注意,无论如何,为了保持代码简单(避免重复检查),客户端仍然可以在观察者到期的exect时刻调用onCall()方法,观察者可以在那之后立即调用清理。所以我们在客户端调用和清理操作之间有时间= 0.(在下一次客户端调用时,另一个观察者将被垃圾邮件)
package stackOverflow;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Service {
private ServiceWatch watch = new ServiceWatch(this::onIdle, 5000L);
public void compute() {
// business here
// and notify the watcher
watch.onCall();
}
private void onIdle() {
// cleanup here, will be executed by the watcher
}
}
class ServiceWatch {
boolean started;
volatile long lastCallTime;
private Runnable onIdle;
private long maxIdle;
private ExecutorService es = Executors.newSingleThreadExecutor();
public ServiceWatch(Runnable onIdle, long maxIdle) {
this.onIdle = onIdle;
this.maxIdle = maxIdle;
}
public synchronized void onCall() {
lastCallTime = System.currentTimeMillis();
if (!started) {
startWatcher();
started = true;
}
}
// spawns a worker-watcher
public void startWatcher() {
es.submit(
new Runnable() {
private void triggerIdle() {
synchronized (ServiceWatch.this) {
onIdle.run();
started = false;
}
}
@Override
public void run() {
long toSleep;
do {
toSleep = getSleepTime();
if (toSleep > 0) {
try {
Thread.sleep(toSleep);
} catch (InterruptedException e) {
triggerIdle();
toSleep = 0;
}
} else {
triggerIdle();
}
} while (toSleep>0);
}
});
}
private long getSleepTime() {
return (lastCallTime + maxIdle) - System.currentTimeMillis();
}
}