使用循环在新线程中运行我的程序 - 不要使用睡眠?

时间:2013-06-23 23:07:13

标签: java multithreading loops sleep

我只有一个程序用于在它自己的线程中执行此操作:

public void run(){
    long lastTime = System.nanoTime();
    float lastSleep = 0;
    //Everything is in seconds.
    while(running){
        float delta = (System.nanoTime()-lastTime)/1000000000f;
        lastTime = System.nanoTime();
        manager.update(delta);
        panel.repaint();
        lastSleep = Math.max(maxTicSpeed-(delta-lastSleep),5/1000f);
        try{
            Thread.sleep((long) Math.round(lastSleep*1000));
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }
}

基本上我在这样循环时总是被教导睡觉,所以我做了,我的程序至少睡了5毫秒,或者它可以睡觉的最长时间而不超过限制(1/30秒)。但我正在四处看书,睡觉听起来不是很好。

http://msmvps.com/blogs/peterritchie/archive/2007/04/26/thread-sleep-is-a-sign-of-a-poorly-designed-program.aspx

从他说的话来看,如果它太接近睡眠下限,我的程序甚至都不会睡觉,等等。当system.printing时间变化时,变化的范围大约是.31508 - .03475,这对我来说非常好,因为我的程序会导致不准确。

话虽如此,我还能做些什么呢?我正在考虑添加类似这样的东西,而不是尝试{Sleep}:

long waitTill = (long) (System.nanoTime()+lastTime/1000000000f), 
    now = System.nanoTime();
while(now < waitTill){
    now = System.nanoTime();
}

但我的线程不会占用相同数量的处理器时间吗?我认为重点是阻止我们的线程占用更多的处理器而不是实际需要..

那么,我应该使用睡眠(最小睡眠时间更长?),我应该使用我的替代方案,我应该使用其他替代方案,还是应该让我的程序以无限制的速度循环?即使我考虑到睡眠不准确,我的编程也很差吗?

感谢您的帮助!

修改 因此,定时器已被推荐,但我明白,如果我的任务没有在Timer再次调用之前完成,那么我会遇到问题。这对我的计划来说是一个明确的问题。我觉得我已经通过使用delta处理了Thread.sleep()的问题,那么Thread.sleep()会像以前那样更好吗?

5 个答案:

答案 0 :(得分:5)

要解决此问题,您需要重新考虑您的设计。基本上你正在做的是定期安排工作。运行/静止/睡眠模式有效,但正如您的研究发现的不是最佳解决方案。

现代语言具有“任务运行”模式,允许编程环境/操作系统更好地管理任务的执行。

在Java中,有java.util.timerjava.util.timertask。使用任务运行模式,您可以创建任务,并将其安排为以特定间隔运行。

通过取消计时器,计时器还为您提供了一种更简洁的方法来停止执行循环,而不是设置布尔标志。

来自评论:

有一些人应该注意的问题。如果您的任务是任务运行时间超过计划间隔的任务,则可以在上一个任务仍在执行时运行另一个任务。一些解决方案:

  1. 使用作业队列排队要完成的工作。如果队列中没有工作,则任务返回。
  2. javascript中常见的另一种方法是安排任务执行一次,并在任务结束时重新安排该任务执行一次。
  3. 一种不太优雅的方法,使用标志来指示正在执行的特定任务。这有效,但需要您正确管理标志的状态,这很容易出错。
  4. 另一个常见问题是计划的计时器通常是尽力而为。也就是说,OS / Framework尝试按计划运行任务,但不保证任务将在指定的时间间隔内完全执行。因此,如果您的任务需要严格的确定性调度,您可能需要更接近操作系统/硬件解决方案。

答案 1 :(得分:1)

你绝对应该避免纯粹旋转(你的第二个例子)(除了某些特定用例,例如同步原语),因为它消耗CPU 只做等待而且很容易导致死锁

这就是为什么你经常使用 sleep 延迟消耗更少的CPU并为其他线程提供更多工作机会,从而降低死锁的风险。

只要你知道自己在做什么,你的第一个循环就没有问题。

但您应该更喜欢专用于这类用例的组件:计时器

答案 2 :(得分:0)

正如Alan和Pragmateek都说的那样,Timers通常比使用Thread.sleep()更受欢迎,但正如Martin James所说的那样“Sleep()肯定会被滥用,而且经常会被误用,但这并不会使它本质上不正确或某种反模式“。

我的结论是,由于我使用delta,我处理Thread.sleep()的不准确。话虽如此,使用它没有问题。计时器实现起来会有点困难,因为我的任务需要在再次调用计时器之前完成,这对我的程序来说将非常难以保证。

因此,在这个应用程序中,以及我使用它的方式,Thread.sleep()是一个非常可行的选项。

答案 3 :(得分:0)

而不是sleep(),最好使用wait()和notify()。

当使用sleep()时,线程退出正在运行的队列并再次运行该线程,OS已经做了一些额外的工作,这是一个纯粹的开销。

但是当线程正在等待时,它不会退出正在运行的队列,因此没有开销。

答案 4 :(得分:0)

当提出问题时,如果计时器任务的时间超过间隔,计时器的行为如何。

我编写了一个愚蠢的示例程序来观察计时器的行为:

public class FlawedTimerTask extends TimerTask {
    final int taskId;
    final long sleepTime;
    int counter = 0;

    public FlawedTimerTask(int taskId, long sleepTime) {
        super();
        this.taskId = taskId;
        this.sleepTime = sleepTime;
    }

    @Override
    public void run() {
        long beginTimeInNs = System.nanoTime();
        System.out.println("taskId=" + taskId + ", run=" + counter + ", beginning at " + (beginTimeInNs-beginOfExperiment));
        try {
            Thread.sleep(sleepTime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long endTimeInNs = System.nanoTime();
        System.out.println("taskId=" + taskId + ", run=" + counter + ", ending at " + (endTimeInNs-beginOfExperiment));
        counter++;
    }

    static long beginOfExperiment;

    public static void main(String[] args) {
        beginOfExperiment = System.nanoTime();
        Timer timer = new Timer();
        timer.schedule(new FlawedTimerTask(1, 800), 500, 500);
        timer.schedule(new FlawedTimerTask(2, 1000), 500, 2500);
    }
}

输出结果为:

taskId=1, run=0, beginning at 491988762
taskId=1, run=0, ending at 1291944877
taskId=2, run=0, beginning at 1292056514
taskId=2, run=0, ending at 2293928680
taskId=1, run=1, beginning at 2294036467
taskId=1, run=1, ending at 3094967160
taskId=1, run=2, beginning at 3095097404
taskId=1, run=2, ending at 3894902745
taskId=1, run=3, beginning at 3895045820
taskId=1, run=3, ending at 4695902088
taskId=2, run=1, beginning at 4696095849
taskId=2, run=1, ending at 5695887973
taskId=1, run=4, beginning at 5695991911
taskId=1, run=4, ending at 6496896941
taskId=1, run=5, beginning at 6497002803
taskId=1, run=5, ending at 7297814161
taskId=1, run=6, beginning at 7297998297
taskId=1, run=6, ending at 8098803239
taskId=2, run=2, beginning at 8098922575
taskId=2, run=2, ending at 9098814787
taskId=1, run=7, beginning at 9098971977
taskId=1, run=7, ending at 9899803866
taskId=1, run=8, beginning at 9899970038
taskId=1, run=8, ending at 10699807458
taskId=1, run=9, beginning at 10699912038
taskId=1, run=9, ending at 11500693882
taskId=2, run=3, beginning at 11500815143
taskId=2, run=3, ending at 12501656270
taskId=1, run=10, beginning at 12501781380
taskId=1, run=10, ending at 13302714640
taskId=1, run=11, beginning at 13302888511
taskId=1, run=11, ending at 14102727215
taskId=1, run=12, beginning at 14102929958
taskId=1, run=12, ending at 14903695762
taskId=2, run=4, beginning at 14903878616
taskId=2, run=4, ending at 15903607223
taskId=1, run=13, beginning at 15903775961
taskId=1, run=13, ending at 16705705613
taskId=1, run=14, beginning at 16705798644
taskId=1, run=14, ending at 17505650180
taskId=1, run=15, beginning at 17505881795
taskId=1, run=15, ending at 18306578307
taskId=2, run=5, beginning at 18306718815
taskId=2, run=5, ending at 19306666847
taskId=1, run=16, beginning at 19306757953
taskId=1, run=16, ending at 20107480129
taskId=1, run=17, beginning at 20107580217
taskId=1, run=17, ending at 20907534407
taskId=1, run=18, beginning at 20907640911
taskId=1, run=18, ending at 21709616117
taskId=2, run=6, beginning at 21709784855
taskId=2, run=6, ending at 22709563506
taskId=1, run=19, beginning at 22709664236
taskId=1, run=19, ending at 23510559642
taskId=1, run=20, beginning at 23510653956
taskId=1, run=20, ending at 24310465713
taskId=1, run=21, beginning at 24310572217
taskId=1, run=21, ending at 25111451583
taskId=2, run=7, beginning at 25111549105
taskId=2, run=7, ending at 26111453508
taskId=1, run=22, beginning at 26111544614
taskId=1, run=22, ending at 26913489022
taskId=1, run=23, beginning at 26913629531
taskId=1, run=23, ending at 27713421398
taskId=1, run=24, beginning at 27713577305
taskId=1, run=24, ending at 28514443839
taskId=2, run=8, beginning at 28514550985
taskId=2, run=8, ending at 29514349525
taskId=1, run=25, beginning at 29514496450
taskId=1, run=25, ending at 30315367475
taskId=1, run=26, beginning at 30315469488
taskId=1, run=26, ending at 31115349896
taskId=1, run=27, beginning at 31115475648
taskId=1, run=27, ending at 31917465609
taskId=2, run=9, beginning at 31917563773
taskId=2, run=9, ending at 32917368087
taskId=1, run=28, beginning at 32917524636
taskId=1, run=28, ending at 33718337276
taskId=1, run=29, beginning at 33718481634
taskId=1, run=29, ending at 34518366533
taskId=1, run=30, beginning at 34518459564
taskId=1, run=30, ending at 35319336363
taskId=2, run=10, beginning at 35319516009
taskId=2, run=10, ending at 36319338930
taskId=1, run=31, beginning at 36319440301
taskId=1, run=31, ending at 37121299378
taskId=1, run=32, beginning at 37121403957
taskId=1, run=32, ending at 37921223413
taskId=1, run=33, beginning at 37921324785
taskId=1, run=33, ending at 38722168863
taskId=2, run=11, beginning at 38722270877
taskId=2, run=11, ending at 39722259328

你可以观察到

  • 计时器在计时器任务(800毫秒,1000毫秒)的间隔内运行,而不是在500毫秒的间隔内运行
  • 定时器任务之间没有交错(在主循环中有一个阻塞调用,在Timer的实现中是单线程的)