【问题标题】:Thread only running correctly if there is a System.out.println() inside the while true loop只有在 while true 循环中有 System.out.println() 时,线程才能正确运行
【发布时间】:2011-10-31 17:13:23
【问题描述】:

基本上,我正在制作一个更新玩家位置的游戏,它使用这个线程:

@Override
public void run() {
    while(true) {
        System.out.println();
        updatePos(x, y);
    }

}

这很好用,但如果我删除 System.out.println(),它就会停止工作。不知道为什么会这样,全班如下:

public class Player extends Block implements KeyListener, Runnable {

int x;
int y;
int speed;

boolean upPressed; 
boolean downPressed;
boolean rightPressed;
boolean leftPressed;

static Sprite sprite = new Sprite("grass.png");

public Player(int x, int y, int speed) {
    super(x, y, sprite);
    this.x = x;
    this.y = y;
    this.speed = speed;
    Thread playerThread = new Thread(this, "Player Thread");
    playerThread.start();
}

@Override
public void keyPressed(KeyEvent e) {
    if (e.getKeyCode() == KeyEvent.VK_UP) {
        y -= speed;
    }

    if (e.getKeyCode() == KeyEvent.VK_DOWN)
        downPressed = true;

    if (e.getKeyCode() == KeyEvent.VK_RIGHT)
        rightPressed = true;

    if (e.getKeyCode() == KeyEvent.VK_LEFT)
        leftPressed = true;

}

@Override
public void keyReleased(KeyEvent e) {
    if (e.getKeyCode() == KeyEvent.VK_UP)
        upPressed = false;

    if (e.getKeyCode() == KeyEvent.VK_DOWN)
        downPressed = false;

    if (e.getKeyCode() == KeyEvent.VK_RIGHT)
        rightPressed = false;

    if (e.getKeyCode() == KeyEvent.VK_LEFT)
        leftPressed = false;

}

@Override
public void keyTyped(KeyEvent e) {

}

@Override
public void run() {
    while(true) {
        System.out.println();
        updatePos(x, y);
    }

}}

最后说明:Ingore 布尔 upPresses 和 downPressed,我唯一关注的是:

if (e.getKeyCode() == KeyEvent.VK_UP) {
    y -= speed;
}

【问题讨论】:

  • 究竟是什么工作不正常?它运行吗?它是否进入updatePos?您可以使用断点来找出答案...
  • 您接受了解决问题的答案。但是,您的代码未同步,并且无法在其他一些计算机上运行,​​因此您也应该修复同步问题。此外,从构造函数启动线程被认为是不好的做法。

标签: java multithreading


【解决方案1】:

Thread.yield() 在某些情况下也可以这样做。 例如,请参阅https://www.javamex.com/tutorials/threads/yield.shtml。 但是,此链接的作者不建议使用yield

【讨论】:

    【解决方案2】:

    类似的循环

    while(true) {
        updatePos(x, y);
    }
    

    会完全占用 CPU。使用 println 开始表现更好的原因可能是因为每次 I/O 迭代产生几百个周期。

    我建议你根据所需的帧率添加一个小的睡眠方法:

    while (true) {
        try {
            Thread.sleep(10); // for 100 FPS
        } catch (InterruptedException ignore) {
        }
        updatePos(x, y);
    }
    

    或者,更好的是,采用事件驱动方法,例如java.util.Timer。 (这会更符合 Java 习惯。)

    【讨论】:

    • +1 这可能是因为System.out.println() 非常昂贵,它无意中“修复”了这个问题。
    • @Kublai:可能更多,因为System.out.println 是同步的。
    • @Chris 为了使 GUI 线程和正在运行的线程之间的同步有效,GUI 线程中的某些东西有时必须锁定 System.out.println 使用的同一个锁(无论那个锁可能是)。
    • 即使是sleep(0) 也对我有用。还不知道为什么。但是如果没有sleep(x),当线程打算停止时,线程中的while循环之后的指令将永远不会被调用。使用sleep(x)(即使使用sleep(0)),当我停止线程时会调用循环后的指令。这很有趣。
    • 哦,我在上面找到了我自己的问题的答案:stackoverflow.com/questions/1600572/…
    【解决方案3】:

    还有另一种方法。您可以使用专门用于执行计划任务的类 java.util.Timer。例如,如果您现在需要 2 个线程来定期执行某种不同的操作,这将非常有用。使用 Timer,您只需使用一个线程即可节省您的精力和计算机资源

    【讨论】:

      【解决方案4】:

      您没有将代码发布到updatePos。但至少,您需要使用该方法以及 keyPressed(以及任何其他使用 x 和/或 ysynchronized

      您也可以同时设置xy volatile,但随后xy 可能会同步更新。

      但如果没有任何内存同步,则不能保证来自一个线程的更改可以从另一个线程观察到。

      【讨论】:

      • @Deza 如果你对线程和同步不太了解的话,更详细的解释:keyPressed是从Swing线程调用的,所以y的值在那个线程中改变了(缓存在您的一个 cpu 核心上具有新的 y 值)。但是,运行runupdatePos 的线程可能运行在另一个cpu 内核上,本地缓存不知道它应该更新y(除非你同步你的代码)。
      【解决方案5】:

      不知道这是否会解决它,但看起来你给 JVM 带来了困难...... 尝试在每次 updatePos(x, y) 调用之前添加一个短暂的睡眠(这是实现游戏线程的正确方法)。

      @Override
      public void run() {
          while(true) {
          Try
          {
              Thread.sleep(50);
          }
          catch (Exception e){}
      
          updatePos(x, y);
          }
      

      }}

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-21
        • 1970-01-01
        相关资源
        最近更新 更多