【问题标题】:Resetting a task timer to 0 after method call方法调用后将任务计时器重置为 0
【发布时间】:2015-12-19 20:04:43
【问题描述】:

这是我尝试查找但找不到与我正在尝试做的事情足够相似的情况。

我有一个类是 Web 服务连接的连接管理器,就像这样(为演示目的而简化)

public class ConnectionManager {

    Webservice webservice = null;

    public Webservice getWebservice() {
        if(webservice == null) {
           synchronize(ConnectionManager.class) {
              if(webservice == null) {
                 webService = new Webservice();
              }
           }
        }
        return webservice;
    }
}

我想添加一个进程以在 30 分钟不活动后重置 Web 服务连接。我计划使用 Timer 和 TimerTask 来执行此操作,因为我只需要 1 个进程来重置 Web 服务。

private class ResetServiceTimerTask extends TimerTask {
    @Override
    public void run() {
        webservice = null;
        logger.warn("resetting the web service connections");
    }
}

然后初始化它看起来像这样:

private Timer timer;

public ConnectionManager() {
    timer = new Timer(true);
    timer.schedule(new ResetServiceTimerTask(), 1800000);
}

我的问题是,如果调用 get() 方法,如何将 Timer 倒计时重置为 0?我知道我可以简单地取消当前的计时器任务并安排一个新的,但这似乎很浪费。

我尝试过研究执行器和线程池,但由于某种原因,我无法掌握如何正确地使用这些实现这个用例。如果有人有一个很好的例子来链接让它工作,我很乐意看看它。从其他一些帖子中,我知道建议使用线程池而不是为此使用 Timer/TimerTask。

【问题讨论】:

  • 我认为你需要重新设计它,所以'ConnectionManager'类将负责将'webservice'实例设置为null,否则在调用'getWebservice'方法时可能会得到null。
  • @Victor Bouhnik 不会在类上同步然后验证变量是否为空就足以防止空返回?我认为这类似于其他人如何为单例类调用 getInstance() 的示例。

标签: java timer


【解决方案1】:

我建议您首先采取最安全的解决方案:每次调用 get 方法时,取消当前的 timerTask 并实例化一个新的 timerTask,并设置一个新的超时。

然后,您可以优化以保存 TimerTask:例如,如果下一次超时将在 不到一分钟(或您决定的最小间隔)之后,您可以避免创建新的 TimerTask上一个。我注意到您的超时时间是半小时(1800000 毫秒)。那么,显然,精度不是问题。

可以这样:

private static final int MINIMUM_TIME_LAPSE=60000; // 1 min

private static final int TIMEOUT=1800000; // 30 min

private long previousTime;

private TimerTask timerTask;

private synchronized void scheduleTimeoutIfNecessary()
{
    long currentTime=System.currentTimeMillis();
    if (currentTime-this.previousTime >= MINIMUM_TIME_LAPSE)
    {
        if (this.timerTask!=null)
        {
            this.timerTask.cancel();
        }
        this.timerTask=new ResetServiceTimerTask());
        timer.schedule(this.timerTask, TIMEOUT);
        this.previousTime=currentTime;
    }
}

顺便说一句:我还建议您同步每次访问webervice,以避免在多线程环境中运行条件。因此,它必须声明为私有:

public class ConnectionManager {

    private Webservice webservice = null;

    public void reset() {
        synchronize(ConnectionManager.class) {
            webService = null;
        }
    }

    public Webservice getWebservice() {
        synchronize(ConnectionManager.class) {
            if(webservice == null) {
               webService = new Webservice();
            }
        }
        return webservice;
    }
}

【讨论】:

  • 首先感谢,这实际上解决了我预见的下一个问题之一,即如果 get() 方法在多线程或快速重复中被调用,如何不实例化/取消计时器任务。
  • 其次,webservice 连接已经是私有的。我忘了在我的示例代码中包含它。如果我将该变量标记为 volatile,我是否仍需要在检查它是否为 null 之前同步该类,或者我可以按照我的示例中的方式执行此操作吗?
  • 虽然我对 volatile 变量没有太多经验,但恐怕是的:无论如何你都必须进行外部同步。原因是为了避免第二个线程将webservice 变量设置为空就在 if (webservice==null)return webservice 之间正在第一个线程中执行。
猜你喜欢
  • 1970-01-01
  • 2015-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多