Reputation: 43
I have a question how to implement a variation of whats found here:
Set Time Limit on User Input (Scanner) Java
In my case, I would like to ignore the input if the Timelimit is reached while keeping the program alive.
String str = "";
TimerTask task = new TimerTask(){
public void run(){
if(str.equals("")){
System.out.println("No Valid Input detected");
//TimerTask should end here along with the Input
//Other example has System.exit which will also terminate the
//entire program
}else {
// still keep the program alive. The condition itself isn't
//important.
}
}
Timer timer = new Timer();
timer.schedule(task, 10*1000);
Scanner scanner = new Scanner(System.in);
do{
System.out.println("Type a message");
str = scanner.nextLine();
}while(!str.equals("hello"));
timer.cancel();
REACHED HERE!
If the input is given within 10 seconds(and it's valid), the loop ends and the task is canceled, which is perfect. However, if the input is not valid and the timer ends, I would like for it to stop asking for input and skip to the "REACHED HERE" position. Is that even possible?
Upvotes: 4
Views: 433
Reputation: 10931
As @Sedrick mentions, the simplest solution to this is a second thread. The problem is that reading from System.in
is blocking. Clearly the example you linked to solves that problem with a System.exit()
, but that's too extreme for your case.
Another spin on it might be to use a Deque
(double-ended queue) to relay the input, with the timeout on there:
BlockingDeque<String> deque = new LinkedBlockingDeque<>();
new Thread(() -> {
Scanner scanner = new Scanner(System.in);
String input;
do {
System.out.println("Type a message");
input = scanner.nextLine();
deque.add(input);
} while (!input.equals("hello"));
}).start();
String str;
do {
str = deque.poll(10, TimeUnit.SECONDS);
} while (str != null && !str.equals("hello"));
System.out.println("REACHED HERE!");
Expanded answer...
The idea above was to only create the thread once, and re-use the deque
as the proxy for System.in
. But, in the thread, the read from System.in will always be blocking - there's no clean way to interrupt the thread, short of System.exit()
.
This can be refined a bit though. Firstly, if the thread is marked as a daemon thread, this allows the JVM to shutdown around it still. E.g. if the main()
method completes, the JVM will exit cleanly too:
Thread thread = new Thread(() -> {
...
});
thread.setDaemon(true);
thread.start();
However, by using InputStream.available()
, it is possible to poll for waiting input. This then makes it possible to interrupt the thread cleanly:
BlockingDeque<String> deque = new LinkedBlockingDeque<>();
Thread thread = new Thread(() -> {
Scanner scanner = new Scanner(System.in);
String input;
try {
do {
if (System.in.available() > 0) {
input = scanner.nextLine();
deque.add(input);
} else
try {
Thread.sleep(50);
} catch (InterruptedException ex) {
System.err.println("Thread stopped");
break;
}
} while (true);
} catch (IOException ex) {
ex.printStackTrace();
}
});
thread.start();
System.out.println("Type a message");
String str;
do {
str = deque.poll(10, TimeUnit.SECONDS);
} while (str != null && !str.equals("hello"));
System.out.println("REACHED HERE!");
thread.interrupt();
It looks like there's some risk with the user typing a few letters without a line feed. At the moment that would still hang, but this didn't happen on Windows - apparently data from the command window is only released to System.in
line by line.
Upvotes: 2