Java中的Heartbeat:timerTask还是thread.sleep()?

时间:2016-03-09 14:29:07

标签: java multithreading

我想在java中实现一个非常简单的客户端服务器心跳。最简单的方法似乎是通过睡眠。考虑下面的元代码

class MyClass

    Thread heartbeatThread = new Thread();

    public void() startHeartBeat{
         Thread.sleep(4000);
         sock.write("H");
      }

这是一个适当的解决方案,还是我不考虑陷阱?

我也考虑使用java.util.Timer.scheduleAtFixedRate方法。这会更强大/可靠吗?如果是这样,为什么?这是一个例子(它不是干净的IMO):

class HeartBeat
{
    Timer timer=new Timer();

    public void scheduleHeartBeat(int delay, int period) {
       timer.scheduleAtFixedRate( new HeartBeatTask(), delay, period);     
       }
}   

class HeartBeatTaskextends TimerTask {
     public void run() {
     sock.write("H");     
}

第二种方法会获得更高的优先级吗?

3 个答案:

答案 0 :(得分:12)

首先,基于Thread的习语不会在没有无限循环的情况下以固定费率安排。

这也是一个缺点:你可能想设置一些退出循环的条件。

在调用静态InterruptedException时,您还需要捕获Thread.sleep

计划执行的另一个流行习语是使用ScheduledExecutorService

找到以下3个替代方案:

<强>定时器

// says "foo" every half second
Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
    @Override
    public void run() {
        System.out.println("foo");
    }
}, 0, 500);
  • 优点:简单
  • 缺点
  

在固定速率执行中,每次执行都是相对于初始执行的预定执行时间进行调度的。如果由于任何原因(例如垃圾收集或其他后台活动)延迟执行,则会快速连续执行两次或更多次执行以“赶上”。

文档here

无限循环

new Thread() {
    @Override
    public void run() {
        while (true) {
            // Says "blah" every half second
            System.out.println("blah");
            try {
                Thread.sleep(500);
            } catch (InterruptedException ie) {
                // nope
            }
        }
    }
}.start();
  • 优点:非常简单。您可以通过编程方式改变经常性延迟。
  • 缺点Thread.sleep仍然是
  

受制于系统定时器和调度程序的精确性和准确性。   ......并且需要捕捉InterruptedException

文档here

此外:

  • 你的无限循环可能需要(某种程度上可能很麻烦)破坏条件
  • 没有初始延迟设置,除非在无限循环之前手动应用,这将需要另一个try / catch

<强>执行人

ScheduledExecutorService es = Executors.newSingleThreadScheduledExecutor();
es.scheduleAtFixedRate(
    new Runnable() {
        @Override
        public void run() {
            // Says "bar" every half second
            System.out.println("bar");
        }
    }, 
    0, 500, TimeUnit.MILLISECONDS);
  • 优点:这是3的最新功能。非常简单和优雅 - 您也可以安排Callable s(虽然不是固定费率)并重新使用{ {1}}。 ExecutorService的文档实际上提及java.util.Timer(实施ScheduledThreadPoolExecutor界面)作为“ScheduledExecutorService / Timer组合的更多功能替代品”。
  • 记录
  • 缺点
  

如果执行此任务的时间超过其期限,则后续执行可能会延迟,

文档here

答案 1 :(得分:1)

如果您使用睡眠方法,则需要考虑一些问题。

一个是睡眠时间不准确,你可能会随着时间的推移而漂移(也许当你的线程正在睡觉时,另一个应用程序占用CPU并且你的线程需要比预期更长的时间来发送它的心跳,现在下一次线程发送心跳延迟),你的睡眠时间将逐渐增加各种事物(你不会睡眠时间少于你的睡眠时间,但可能经常睡觉多一点),这些增量会随着时间的推移而增加。

另一个问题是你可能遇到套接字问题,你必须编写代码来处理新的连接。

线程需要表现良好并响应中断,否则就是守护程序线程。如果必须跨线程共享数据,则需要了解内存可见性问题。

使用计时器意味着每次启动任务都会有一个新的开始,你不会容易受到累积的延迟或过时的网络连接的影响。

答案 2 :(得分:1)

是的,我不知道计时器是如何在内部实现的,但我在这里理解的是,如果你使用sleep,你将不得不处理InterruptedException,并且吃掉这个异常可能不是一个好习惯。此外,计时器任务将在其线程空间内运行,您可以更好地控制它。

如果需要,您可以随时停止计时器,在这种情况下,您可能无法执行此操作