5

我正在用java做乒乓球

如果球出界,pause则设置为true

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

哪个应该让计时器休眠...在线程休眠 1000 毫秒后,暂停将设置为 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);  
}

球类 AutoMove() 函数:

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

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

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

谢谢!

4

3 回答 3

6

该指令

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) 底层的系统时钟是准确的)。由于上述原因,如果计划的第一次是在过去,那么任何“错过”的执行都将被安排立即“赶上”执行。

于 2013-05-28T18:40:33.433 回答
2

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

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

于 2013-05-28T18:39:27.163 回答
0

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

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

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

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

synchronized ( pause    ){
    return  pause = newPauseValue;    
}
于 2013-05-28T18:45:52.273 回答