如何在java中使用wait()并正确通知线程?

时间:2017-02-07 12:35:59

标签: java android multithreading

我正在Android工作室制作2D游戏,现在我已经在问题上待了几天。我想停止我的线程Gravity,这样我的玩家就可以跳。当玩家完成跳跃时,重力线程可以继续。

我在互联网上搜索了如何将wait()和notify()与同步块一起使用。但是当我尝试应用它时,我的重力不会停止。如果有人能告诉我如何以正确的方式使用它,我会非常高兴...

这是我在我的场景类中开始跳跃的代码。

public void recieveTouch(MotionEvent event) {

    switch(event.getAction()){
        case MotionEvent.ACTION_DOWN:
            if(player.getPoint().y > 0) {
                playerJump();
            }
    }
}

public void playerJump(){
    playerJump = new Jump(player, this);
    thread = new Thread(playerJump);
    thread.setDaemon(true);
    thread.start();
}

这是我的重力线程。

public class Gravity implements Runnable {

private Player player;
private GameScene gameScene;

public Gravity (Player player, GameScene gameScene){
    this.player = player;
    this.gameScene = gameScene;
}

@Override
public void run(){
    while(true){

        player.playerMoveDown(3);
        gameScene.update();

        try {
            Thread.sleep(25);
        }
        catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

这是我的跳线。

public class Jump implements Runnable {

private Player player;
private GameScene gameScene;

public Jump (Player player, GameScene gameScene){
    this.player = player;
    this.gameScene = gameScene;
}

@Override
public void run(){
    int eindHoogte = player.getPoint().y - 60;

        while (player.getPoint().y > eindHoogte) {
            player.playerMoveUp(3);
            gameScene.update();

            try {
                Thread.sleep(25);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    }
}

3 个答案:

答案 0 :(得分:1)

假设您将同一个玩家对象传递给Jump和Gravity类,您可以在播放器上进行同步并保持跳转完成操作的标志。

这将确保在玩家执行跳跃操作时重力将等待跳跃操作,并且只有在玩家跳跃时才会再次继续

标志仅用于防止当另一个线程没有调用wait()时错过通过一个线程调用notify()方法发送的通知的情况

请参阅下面的示例代码

    public class Jump implements Runnable {

    private Player player;
    private GameScene gameScene;

    public Jump (Player player, GameScene gameScene){
        this.player = player;
        this.gameScene = gameScene;
    }

    @Override
    public void run(){
        int eindHoogte = player.getPoint().y - 60;

        while (player.getPoint().y > eindHoogte) {
                player.playerMoveUp(3);
                gameScene.update();

                try {
                    Thread.sleep(25);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
        }

        synchronized(player){

            // acts as a signal to gravity thread to start processing the gravity operation
            player.setJumpComplete(true);
        }

    }

}


    public class Gravity implements Runnable {

        private Player player;
        private GameScene gameScene;

        public Gravity (Player player, GameScene gameScene){
            this.player = player;
            this.gameScene = gameScene;
        }

        @Override
        public void run(){



            while(true){

                synchronized (player) {

                    // if player has completed the jump then process the gravity operation else just wait for jump to complete
                    if(player.isJumpComplete()){
                        player.playerMoveDown(3);
                        gameScene.update();

                        // reset the jump flag and wait for the next jump operation to complete
                        player.setJumpComplete(false);
                    }

                }

                try {
                    Thread.sleep(25);
                }
                catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        }

答案 1 :(得分:0)

让我给你一个通用答案,这不仅对这种情况有帮助,而且对于将来这可以帮助gou作为迷你指南。使用Java语言时,多线程的非常基本元素是

  1. 同步块

  2. 一个对象,lock(为了方便起见,我们可以这样命名),你可以在其上应用synchronized块来获取监视器。

  3. wait()对象方法,lock

  4. notify()notifyAll()对象方法,lock

  5. Thread个对象,可能需要实现Runnable intetface的类。

  6. 需要注意的事项如下。

    1. 您应该只对当前线程获取监视器的对象调用wait(),notify()或notifyAll()。

      1. 1 sleep()是Thread.java的方法,wait()是Object.java 的方法。
      2. 1.2 sleep()如果已获取锁定则不放弃锁定,而wait()放弃锁定。在锁定之后调用睡眠并不是强制性的,等待它是强制性的。

        1.3 也更喜欢notifyAll()而不是notify(),因为notify可能只发出任何单个线程要唤醒并尝试获取锁定,而notifyAll()唤醒所有符合条件的waitng线程并允许获得锁定的竞争,因此通知有更好的机会获得所有线程的平等机会

      3. 通常,线程执行一次进程并调用notifyAll()并等待(),直到再次通知为止。同样,如果它被中断,通常的方法是终止线程。在执行wait方法时,Thread会放弃监视器,因为它在调用notifyAll方法之前调用了lock对象上的wait方法,因此所有其他有资格获取锁定监视器的线程都被唤醒。线程调度程序将监视器分配给其中一个线程,它同时执行一个命令,在锁定时调用notifyAll,然后调用wait on lock。现在第二个线程也进入等待状态以重新启动监视器,其他一些线程同时获取监视器,整个过程继续,直到我们不中断所有线程。

      4. 这样一个模型的例子你可以参考我为另一个问题写的答案

        https://stackoverflow.com/a/42049397/504133

        请确保在使用android时,不要从主GUI线程以外的任何其他线程修改主GUI,如果您希望这样做,请使用处理程序

答案 2 :(得分:0)

关于通知/等待的主题,上面的答案是合适的,所以我不会在这里添加任何内容,但我认为你可以用另一种方式解决你的问题,通过更像现实生活来建模。 重力是作用于玩家的外力,而重力线程修改玩家位置确实有意义,但跳跃并不是真的相同。跳跃是玩家自己做的事情,因此它是玩家对象的属性。

我会将设计重新设计为:   - 为玩家提供一个动作属性,它是一个效果对象列表,一个是重力效果,一个是跳跃效果,一个是步行效果,每个都提供一些运动偏移,同时处于活动状态。   - 将Gravity线程转换为Motion线程,该线程从每个玩家效果中检索移动偏移,将其加起来然后移动玩家总数 - 跳转事件只需打开或关闭跳跃效果 - 除非玩家获得反重力套装或其他东西,否则不要使用重力效果

因为我在手机上输入了这个伪代码,所以

interface Effect {
   method Point getMotion(duration);
   method Boolean isCompleted();
}

class Jump implements Effect {
  ///...
}

class sceneObject {  

  synchronized  List<Effect> movementEffects;

  Point position;

  void move(Point);

  void addEffect(Effect);

  void removeEfects(Class);

   void Point getTotalMotion(duration) {
       Point result;
       for each effect in movementEffects {
              result += effect. getMotion(this, duration);
              if effect.isCompleted() {
                     removeEffect(effect)
              }
       }
       return result;
    }
    void applyMotion(duration) {
         move(getTotalMotion(duration))
    }
}

class player extends SceneObject {

   Effect gravity = new Gravity(3);

   Player () {
           addEffect(gravity);
   }

    void jump() {
           addEffect(new Jump)
    }

    void walkLeft() {
           stopWalking();
           addEffect(new Walk(-3))
    }

    void stopWalking() {
           removeEffects(Walk.class)
   }
}

class EffectsThread {
      void run() {
          While (...)  {
                timeSinceLastIteration = ... ;

                for each item in scene {
                      item.applyMotion(timeSinceLastIteration);
                }
                //sleep
           }
      }
}

然后你也只需要一个效果线程来运行,你永远不需要启动和停止它