简体   繁体   中英

Java: How to break loop when using Executorservice and future for timer?

I'm trying to make a timer for a simple math quiz where user needs to answer the question within 10 seconds, and when that time exceeds, it will load to the next question. I found the solution to implement Executorservice and future, but I'm having a problem when it keeps repeating the loop for invalid input (numberformat exception) when user enter other input than integers at my Game class.

Hope this is useful to know: class MathQuestion (where the math questions are generated), Game (the game round initiated), and GameTimer.

Game class :-

  //for 10 rounds of game
   for (int i=0; i<rounds; ++i)
   {
       System.out.println("*************************************");
       System.out.println("\nRound " + (i+1)
               + "\nEnter 'x' to quit game");

       System.out.println("\nQuestion:");
      //call method to generate question and get correct answer
      result = quiz.Questions();
      System.out.println("You have 10 seconds");

     //to make sure we read a line or interrupt it for the timer
     ExecutorService ex = Executors.newSingleThreadExecutor();
      try
     {
     //retrieve the actual result at a later point in time.
     Future<String> ans = ex.submit(new GameTimer());
     try {

      boolean validInput = false;

      do
      {

           System.out.println("\nEnter your answer: ");//prompt user input answer
          //10 secs wait to obtain result from player
          //wait until callable complete before return result
            answer = ans.get(10, TimeUnit.SECONDS);
           //answer = scan.nextLine();
           try
           {
               //valid player input
               pAnswer = Double.parseDouble(answer.trim()) ; //convert to double
               validInput = true;

           }catch (NumberFormatException e){ //other input other than integers *this the repeated loop  
            System.out.println("Invalid input. Please try again.");
            validInput = false;
            ans = ex.submit(new GameTimer()); //resubmit task solution
            }

           if (answer.trim().equalsIgnoreCase("x")) //player quit game
           {
               quit=true;
               break;
           }

      }while (!validInput);
    if (quit)
    {
     break;
    }
            //for correct answer
            if (Math.abs(Double.parseDouble(answer) - quiz.answer)  <= 0.01) {
            //add 10 points
            this.gamer.setScore(score+=10); 
            System.out.println("You got it right!");
        } 
        else //wrong answwer
        {
            //subtract 10 points
            this.gamer.setScore(score-=10);;
            System.out.println("You got it wrong!");

        }
        } catch (InterruptedException ex1) {
            Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex1);
        } catch (ExecutionException ex1) {
            Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex1);
        } catch (TimeoutException ex1) {
            System.out.println ("\nTime's up! Better luck next time~");
        }
    }finally {
      ex.shutdownNow(); //stop all running tasks and shut the executor down immediately.
    }

    }     

GameTimer class :-

public class GameTimer implements Callable<String>  {


public String call() throws IOException {
BufferedReader inp = new BufferedReader(new InputStreamReader(System.in));
String input = "";
while ("".equals(input)) {
  try {
    while (!inp.ready()) {
      Thread.sleep(1000);
    }
    input = inp.readLine();
  } catch (InterruptedException e) {
    return null;
  }
} 
return input;
}
}

Here is a picture of the output loop problem: loop problem

I have been trying to find solutions for timer a really long while now, I would really appreciate it if anyone could help. Thank you so much.

*additional : Is there a way to display that timer countdown (eg: 10 9 8..) but with erasing the previous second when the new second count comes up?

(without erasing suggestion would also help, thank you! )

You cannot keep re-calling get. Your task is an executable, you submit it and it gets run. You call .get and it gets the result of your execution. You need to re-submit the task, for example:

catch (NumberFormatException e){ //other input other than integers *this the repeated loop  
    System.out.println("Invalid input. Please try again.");
    validInput = false;
    ans = ex.submit(new GameTimer());
}

You might want to keep track of the time elapsed since each time you call get you're using the same timeout.

In my opinion, you should put the error handling in your callable.

final String question = "Enter double value";
Callable<Double> getDouble = ()-{

     BufferedReader inp = new BufferedReader(neww InputStreamReader(System.in));
     while (!Thread.currentThread().isInterupted()) {
         System.out.println(question);
         try {
             String input = inp.readLine();
             Double v = Double.parseDouble(v);
             return v;
         } catch(Exception e){
             if( Thread.currentThread().isInterrupted()){
                 return null;
             }
             System.out.println("Invalid Input, try again");
         }
     } 

}

Now when you submit the task, you don't have to worry about an invalid double because it is handled in the callable task.

Future<Double> ans = ex.submit(getDouble);
Double v = ans.get(10, TimeUnit.SECONDS);

This will leave one problem: the task, even if canceled will still be waiting on the standard input. The easiest way to fix this is to tell your user to press enter to continue.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM