while循环不循环/显示计数

时间:2015-06-10 19:12:47

标签: java while-loop

此方法重复读取命令并执行它们直到游戏结束。 已完成变量,如果为true,则表示玩家/用户已点击退出并希望结束游戏 - 从而退出循环并执行直至方法结束。

import org.apache.commons.lang3.time.StopWatch;

private Parser parser;
private Command command;
private StopWatch stopWatch;

public void play() 
{   

    stopWatch = new StopWatch();
    stopWatch.start();
    long timeLimit = 5000;

    boolean finished = false;

    printWelcome();

    while (finished == false) 
    {   
       System.out.println(stopWatch.getTime() + " milliseconds");
       if (stopWatch.getTime() >= timeLimit)
       {
          System.out.println("Time limit of " + timeLimit + " milli seconds has been reached. Good bye!");
          return;
       }
       else
       {
          command = parser.getCommand();
          finished = processCommand(command);
       }
    }     
    System.out.println("Thank you for playing.  Good bye.");       
}

但我正在观察循环中的奇怪行为。它完全循环(当省略以下行时显示stopWatch.getTime()的连续计数:

  else
  {
     command = parser.getCommand();
     finished = processCommand(command); 
  }

但是当我把它们放回去时,它会停止显示stopWatch的连续递增时间,因为它朝着timeLimit增加(此时它应该停止)。即使玩家没有输入任何命令或输入。

stopWatch显然在后台运行,但它不显示计数。

请指教。谢谢。

3 个答案:

答案 0 :(得分:3)

  

即使玩家没有输入任何命令或输入。

如果Parser#getCommand是等待用户输入的方法,则执行将阻止,直到用户输入输入。这意味着while循环不会运行,您将看不到它打印出新时间。

无论如何,按照你的要求做会给用户带来非常刺耳的体验,因为这意味着它会在输入命令时继续打印,而且用户不是很友好。

您可以使用producer-consumer approach。我修改了链接问题的代码:

// Shared queue
final Queue<String> commands = new ConcurrentLinkedQueue<>();

// Non-blocking consumer
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleAtFixedRate(new Runnable() {
    @Override
    public void run() {
        // non-blocking
        if((String command = commands.poll()) != null) {
            processCommand(command);
        }
    }
}, 0, 10, TimeUnit.MILLISECONDS);

// Blocking producer (this is basically the logic from Parser#getCommand)
Scanner sc = new Scanner(System.in);
while(sc.hasNext()) {
    commands.add(sc.next());
}

但是,此解决方案实际上并不允许您实现超时。因此,您必须创建一个“包装”Runnable的新类,以便您可以在超时后取消它(类似于this方法):

public class TimeOutableRunnable implements Runnable {

    private final Queue<String> commands;
    private final Runnable wrapped;
    private volatile ScheduledFuture<?> self;

    public TimeOutableRunnable(Runnable wrapped, Queue<String> commands) {         
        this.commands = commands;
        this.wrapped = wrapped;         
    }

    @Override
    public void run() {
        if(commands.isEmpty()) {
            self.cancel(false);
        } else {
            wrapped.run();
        }
    }

    public void runWithTimeout(ScheduledExecutorService executor, long timeout, TimeUnit unit) {
        self = executor.scheduleAtFixedRate(this, 0, timeout, unit);
    }
}

然后你可以这样做:

final Queue<String> commands = new ConcurrentLinkedQueue<>();
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
new TimeOutableRunnable(new Runnable() {
    @Override
    public void run() {
        // non-blocking
        while((String command = commands.poll()) != null) {
            processCommand(command);
        }
    }
}, commands).runWithTimeout(executor, timeLimit, TimeUnit.MILLISECONDS);

我没有对此进行测试,我不会对并发性的评价很高,所以请告诉我这种方法是否存在根本性的错误。

答案 1 :(得分:2)

我认为你的parser.getCommand();在输入之前就会阻塞。由于此while循环在一个线程中运行,此时程序停止。

检查我是否正确的最简单方法是输入任何命令,你应该从这一行获得一些输出:

       System.out.println(stopWatch.getTime() + " milliseconds");

您需要在此方法中实现超时,或者让第二个线程计算时间并中断等待。

答案 2 :(得分:0)

也许我误解了你的问题,但听起来你的问题是它不能连续打印?如果是这样,那是因为你的佩戴者正在等待用户输入。线程可以解决这个问题,运行你的计时器并打印一个单独的线程