Java:如果没有在特定时间内调用方法,则引发事件

时间:2018-05-30 17:47:08

标签: java concurrency

在并发环境中,多个线程调用方法。在该方法未被调用一段时间(例如5秒)之后,应该调用空闲事件。

在没有调用方法的情况下再过5秒钟后,不应再次调用idle事件。应在第一次方法调用后再次启动空闲计时器。

public class Service {
    public void do(Foo foo) {
        // process foo
    }

    private void onIdle() {
    }
}

实施此措施的安全有效方法是什么?

如果没有第二个要求(不要一次又一次地调用onIdle())可以做得更好,那么这是可以接受的。

2 个答案:

答案 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();
  }
}