【问题标题】:Java Pong - timer thread sleep still runs functionJava Pong - 定时器线程睡眠仍然运行功能
【发布时间】:2013-05-23 20:03:04
【问题描述】:

我正在用java做乒乓球

如果球出界,pause 设置为true

    if (ball.getX() <= 0) {
        score2++;   
        pause = true;       
    }
    if (ball.getX() >= this.getWidth()-ballWidth) {
        score1++;
        pause = true;
    }

应该让计时器休眠...在线程休眠 1000 毫秒后,pause 将设置为 false,并且球应该继续移动 (ball.autoMove()):

public void timer() {
    int initialDelay = 1000;

    timer.scheduleAtFixedRate(new TimerTask() {
        public void run() {
            if (pause) {
                try {   
                    ball.reset(width/2, height/2);
                    Thread.sleep(1000);     
                    pause = false;                      
                } 
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            ball.autoMove(velocityX, velocityY);    
            collisionCheck();           
        }
    }, initialDelay, 100);  
}

Ball 类 AutoMove() 函数:

public void autoMove(double velX, double velY) {
    xLoc = this.getX()+velX;    
    yLoc = this.getY()+velY;    
}

它会完成所有这些...它会休眠,暂停设置为 false,等等...但是当球在暂停 1 秒后重置(在屏幕中间重置...)时,它跳到游戏面板的另一边,告诉我当线程“休眠”时,ball.autoMove(velocityX, velocityY); 仍在更新坐标。

为什么会这样?那不应该运行吗?

谢谢!

【问题讨论】:

  • 永远不要在 EDT 中睡觉,尝试使用 Swing 计时器
  • ball.autoMove() 中有什么内容?
  • 什么是EDT?谢谢
  • 您确定widthheight 设置正确吗?
  • EDT 是事件调度线程。阅读更多:stackoverflow.com/questions/7217013/…

标签: java timer


【解决方案1】:

当该线程处于休眠状态时,保证不会调用autoMove。但是,如果不看代码,我敢打赌 autoMove 会根据实时变化移动球。一旦暂停结束并且您再次调用 autoMove,这将使球看起来跳跃。

您需要将 autoMove 更改为基于相对时间,或者您需要更改它使用的时间变量并减去您的暂停时间。

【讨论】:

  • 这不仅仅是猜测。我们知道事实上它不是在睡觉时调用它。因此,在保证这一点的情况下,这是唯一的解释
【解决方案2】:

说明

timer.scheduleAtFixedRate(new TimerTask() {...}, initial, 100 );

在 1 秒的睡眠期间将 10 次 autoMove 添加到计时器队列。

固定费率的安排是正确的,但你不必做任何事情来代替睡觉。

将计数器设置为 10,并在每次调用该方法时将其递减,当它达到零时,继续正常方式(与“暂停”方式相反)。

timer.scheduleAtFixedRate(new TimerTask() {
   public void run() {
      if( pauseCount == 10 ) {
         ball.reset(width/2, height/2);
      }
      if( pauseCount > 0 ) {
         --pauseCount;
      }
      else {
         ball.autoMove( velocityX, velocityY );
         collisionCheck();
      }
   }
}, initialDelay, 100 );

通过睡眠跟踪执行日志。

首先是程序:

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

public class SO {

   static boolean isTrue = true;

   public static void main( String[] args ) {
      new Timer().scheduleAtFixedRate( new TimerTask() {
         @Override public void run() {
            System.out.println(
               System.currentTimeMillis() + ": " + Thread.currentThread());
            try{
               if( isTrue ) {
                  TimeUnit.SECONDS.sleep( 1 );
                  isTrue = false;
               }
            }
            catch( InterruptedException e ){
               e.printStackTrace();
            }
         }
      }, 0, 100 );
   }
}

还有日志,观察时间:

1369769673294: Thread[Timer-0,5,main]
1369769674308: Thread[Timer-0,5,main] <-- #1 at 308
1369769674308: Thread[Timer-0,5,main] <-- #2 at 308
1369769674308: Thread[Timer-0,5,main]
1369769674308: Thread[Timer-0,5,main]
1369769674308: Thread[Timer-0,5,main]
1369769674308: Thread[Timer-0,5,main]
1369769674308: Thread[Timer-0,5,main]
1369769674308: Thread[Timer-0,5,main]
1369769674308: Thread[Timer-0,5,main]
1369769674308: Thread[Timer-0,5,main] <-- #10 at 308, queued during the sleep
1369769674402: Thread[Timer-0,5,main]
1369769674496: Thread[Timer-0,5,main]
1369769674605: Thread[Timer-0,5,main]
1369769674699: Thread[Timer-0,5,main]
1369769674808: Thread[Timer-0,5,main]
1369769674901: Thread[Timer-0,5,main]
1369769674995: Thread[Timer-0,5,main]

我们观察到激活就像排队,但实际上 Timer 类只有一个线程。 它的逻辑是模拟事件队列,java.util.Timer 的文档很清楚:

在固定速率执行中,每次执行都是相对于 初始执​​行的预定执行时间。如果执行是 因任何原因延迟(例如垃圾收集或其他背景 活动),两个或多个处决将快速连续发生 “跟上来。”从长远来看,执行频率将是 正好是指定周期的倒数(假设系统 时钟底层 Object.wait(long) 是准确的)。由于 以上,如果预定的第一次是过去,那么任何 “错过”处决将被安排立即“赶上” 执行。

【讨论】:

  • 真的吗?哇...我该如何解决?
  • @Growler 我认为您应该使用 scheduleWithFixedDelay 代替。
  • 什么时候调用 timer()?如果被重复调用,那么您可以使用@Aubin 的建议并制作变量 volatle 以便在所有线程中读取相同的值。你还没有显示哪个线程暂停是真实的(我猜是 UI 线程?)
  • @Aubin 我不明白为什么我不能使用Thread.sleep() 那么......什么时候可以使用它?
  • 不要混用 Sleep 和 Schedule,第一个暂停一个线程,第二个保证每个 xx 延迟都会调用一个方法。就像撞上一架飞机,坐下来放松一下,即使你什么都不做,飞机也会飞……
【解决方案3】:

很高兴看到更改暂停位置的完整代码。

你怎么知道它是在它睡觉的时候发生的?你可以添加带有时间戳的记录器来确定吗?但我会刺伤。

如果暂停是在另一个线程中设置的,那么您需要对其进行同步,并且如果您将其声明为 volatile 是一个好主意。在此方法中同步以及在其他任何地方设置暂停。

private volatile Boolean pause = false;
//...
//make a setter and getter for pause
synchronized ( pause    ){
    return  pause ;    
}

synchronized ( pause    ){
    return  pause = newPauseValue;    
}

【讨论】:

  • 只使用setter和getter来读取和改变pause的值
  • 我上面显示的是 pause 唯一被改变的地方
猜你喜欢
  • 2020-10-02
  • 1970-01-01
  • 2016-08-20
  • 2013-08-05
  • 2014-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-12
相关资源
最近更新 更多