So I'm trying to read user input using the Scanner class. Is there a simple way to make it so that after 10 seconds it moves onto the next block of code? Thanks
You can use Timer and TimerTask. TimerTask will allow you to run a task after a certain amount of time, in this case you can use this task to stop waiting for the user.
import java.util.Timer;
import java.util.TimerTask;
...
TimerTask task = new TimerTask()
{
public void run()
{
if( str.equals("") )
{
System.out.println( "you input nothing. exit..." );
System.exit( 0 );
}
}
};
...
Timer timer = new Timer();
timer.schedule( task, 10*1000 );
Scanner sc = new Scanner(System.in);
String in = sc.readLine();
timer.cancel();
So if the user doesn't respond within 10 seconds here, the timer will quit reading input. I stole/adapted this response from this initial post:
Time limit for an input
I found another approach where we can continue our flow after the timeout instead of terminating the program.
That can be done via ScheduledExecutorService class.
The logic implemented is
Initialize a variable for managing the input state and another one for storing the result. (The input I wanted was boolean)
Schedule the input task at a 0s delay (i.e ask for input immediately)
Schedule another task to check for the input status as per the specified delay
Wait for both the tasks to be completed
Return the result
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
...
//member variables of class
private static AtomicBoolean inputValue = new AtomicBoolean(true);
private static AtomicBoolean inputEntered = new AtomicBoolean(false);
private static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
...
public static void main(String[] args) {
try {
boolean yes = getTimedInput("Enter yes or no", 5);
} catch (Exception e) {
e.printStackTrace();
}
}
...
public static boolean getTimedInput(String inputString, long timeoutInSeconds) throws Exception {
System.out.printf("%s [Y/n] [timeout=%ds] >> ", inputString.trim(), timeoutInSeconds);
scheduler.schedule(() -> {
try {
String s = br.readLine();
if (s != null) {
inputValue.set(s.trim().equalsIgnoreCase("y"));
}
inputEntered.set(true);
} catch (IOException e) {
}
}, 0, TimeUnit.SECONDS);
scheduler.schedule(() -> {
if (inputEntered.get()) {
inputEntered.set(false);
} else {
System.out.println("\nInput timed out. Considering it as Y");
inputValue.set(true);
}
}, 0, TimeUnit.SECONDS);
scheduler.awaitTermination(timeoutInSeconds + 1, TimeUnit.SECONDS);
System.out.println();
return inputValue.get();
}
Related
Here is a task T:
T is composed of many subtasks, and the completion time of each subtask is different.If one of the subtasks fail, the others should be stoped at once and the task T will fail.
So how to simulate the process of task T? (require fast-failure)
Maybe Future Class could solve it. But how?
The easiest fix to this is to separate out these Runnables into their own threadpool so you can then call shutdownNow() on that pool only which interrupts all of the tasks in that pool
Here's an example using FutureTask.
One of the tasks simulates failure by waiting for 1 second and then cancelling the other tasks. Even though they are waiting for 5 seconds, the program completes in 1 second.
You would need to wrap your Runnable in something like this which can cancel if the actual task fails.
To be able to cancel a task, it needs to be in a state where Thread.interrupt() will stop the thread. If it is just sitting in a loop, for instance, then the Future will be cancelled (so get() will return immediately), but the task itself will keep running.
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
public class FutureTaskExample {
public static void main(String[] args) throws InterruptedException {
List<FutureTask<String>> tasks = new ArrayList<>();
ExecutorService executorService = Executors.newFixedThreadPool(10);
long start = System.currentTimeMillis();
for (int i = 0; i < 10; ++i) {
final int ii = i;
FutureTask<String> task =
new FutureTask<>(new Callable<String>() {
#Override
public String call() throws Exception {
if (ii == 9) {
Thread.sleep(1000);
tasks.subList(0,9).stream().forEach(t -> t.cancel(true));
return "Failure";
} else {
long start = System.currentTimeMillis();
Thread.sleep(5000);
System.out.println("Task " + ii + " slept for " + (System.currentTimeMillis() - start));
return "Completed";
}
}
});
tasks.add(task);
executorService.execute(task);
}
List<String> results = tasks.stream().map(t -> {
try {
return t.get();
} catch (Exception e) {
// ignore
e.printStackTrace();
return "Interrupted";
}
}).collect(Collectors.toList());
System.out.println("Completed in " + (System.currentTimeMillis() - start) + " " + results);
executorService.shutdown();
executorService.awaitTermination(10, TimeUnit.SECONDS);
System.out.println("Done in " + (System.currentTimeMillis() - start));
}
}
I give a simulation example by using CompletableFuture
I create a Task Class TaskT, make it looks more real. And the Task Class contains three state, runTask() method and cancel() method.
Three state are Success, Cancelling and Cancelled.Success means task run completed.Cancelling means task is cancelling. Cancelled means the task has been Cancelled, advice others task cancel quickly.
runTask() method: I use Thread.sleep(interval) to simulate runing state. And when the task run finish, the code if(cancelled) return Result.CANCELLED; detect whether the state is canceled?
cancel() method: I use double-check lock to decorate the "cancel" logic to ensure it is single instance.
Finally, in the main metod, I use CompletableFuture Class to start thread and return param. thenAccept() method is useful and help SUCCESS TASK advice the other RUNNING TASKs cancel the task.
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class Competable {
// three state after run task
static enum Result{
SUCCESS, FAIL, CANCELLED
}
// task list
static List<TaskT> tasks = new ArrayList<>();
/**
* Task List
*/
public static class TaskT{
private String name;
private int timeInSecond;
private Result ret;
volatile boolean cancelling = false;
volatile boolean cancelled = false;
public TaskT(String name, int timeInSecond, Result ret){
this.name = name;
this.timeInSecond = timeInSecond * 1000;
this.ret = ret;
}
/**
* Simulate task runing
* runing time in real work is uncertain
* maybe run in computing,maybe run in IO
*/
public Result runTask(){
int interval = 100;
int total = 0;
try {
for(;;){
Thread.sleep(interval);
total += interval;
if(total>=timeInSecond) break;
if(cancelled) return Result.CANCELLED;
}
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(name + "Task End!!!");
return ret;
}
/**
* Simlulate task cancel
* and set cancel time
*/
public void cancel() {
if (!cancelled) {
synchronized (this) {
if (cancelled) return;
cancelling = true;
System.out.println(name + "cancelling!!!");
try {
TimeUnit.MILLISECONDS.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + "cancelled!!!");
}
cancelled = true;
}
}
}
/**
* rollback: advice the other thread cancel
*/
public static void callback(Result result, TaskT task){
if(Result.FAIL == result){
for(TaskT _task : tasks){
if(_task!=task){
_task.cancel();
}
}
}
}
public static void main(String[] args) throws IOException {
TaskT subtask1 = new TaskT("task1", 3, Result.SUCCESS);
TaskT subtask2 = new TaskT("task2", 4, Result.SUCCESS);
TaskT subtask3 = new TaskT("task3", 1, Result.FAIL);
tasks.add(subtask1);
tasks.add(subtask2);
tasks.add(subtask3);
for(TaskT task:tasks){
CompletableFuture f = CompletableFuture.supplyAsync(()->task.runTask())
.thenAccept((result -> callback(result, task)));
}
// System.in.read();
}
}
Goal
I'm currently building (to practice with java) a basic command line multiplayer turn-based game. In this game, each player has 5 seconds to make his move. When he makes his move (or when the timer ends) the other player starts his turn, etc etc.
The server sends a TimerEnded message every time the timer ends.
My current goal is to achieve flawless input reading that could be interrupted when a TimerEnded message arrives to the client.
Design
To achieve this I created a singleton called InputManager. This class handles all the input reading stuff. I created a method called ask which takes a callback as parameter. In this method I create a new thread and inside it I wait for an input with Scanner.hasNextInt.
This class has also the method closeInput which sends an Interrupt message to the thread described above.
Here's the current implementation of the class:
class InputManager{
private Thread thread;
private InputManager(){}
private static InputManager instance;
private static InputManager getInstance(){
if(instance == null){
instance = new InputManager();
}
return instance;
}
/**
* Ask user to type a number.
* #param onSelected When the user has made his choice, this callback will be executed
*/
public static void ask( Consumer<Integer> onSelected){
getInstance().thread = new Thread(() -> {
System.out.println("Type a number:");
Scanner sc = new Scanner(System.in);
int selection = -1;
while (selection == -1) {
if(Thread.currentThread().isInterrupted()){
return;
}
if(sc.hasNextInt()){
selection = sc.nextInt();
onSelected.accept(selection);
} else {
sc.next();
selection = -1;
}
}
});
getInstance().thread.start();
}
/**
* Reset input stream (?)
*/
public static void closeInput(){
try {
getInstance().thread.interrupt();
} catch(NullPointerException e){
// do nothing
}
}
}
Problem
This code is extremely unreliable. I'll show you what I mean in just a moment.
I made a toy class called Client and in the main I simulated the TimerEnd message income with a timer.
class Client {
/**
* Ask user to type a number and send it to the server
*/
void makeRequest(){
InputManager.closeInput();
InputManager.ask((selected) -> {
System.out.println("Sent message: " + selected);
});
}
public static void main(String[] args) {
Client client = new Client();
client.makeRequest();
// Simulate Server messages
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
System.out.println("Message received");
client.makeRequest();
}
}, 5000, 5000);
}
}
Here's how it works in action:
Type a number:
2
Sent message: 2
Message received
Type a number:
3
Sent message: 3
Message received
Type a number: // Here I don't type anything
Message received
Type a number:
Message received
Type a number:
Message received
Type a number: // Here I can send multiple messages on the same "turn"
1
Sent message: 1
2
Message received
Non-educated guess
Currently, I guess that Scanner remains waiting for input and so the if(isInterrupted) statement is not hit until an input is given. If so, how can I avoid this behaviour?
I understand that this question is extremely (and maybe unnecessarily) long, and since you read it let me thank you for taking your time.
Minimal, Complete and Verifiable code
package com.company;
import java.util.*;
import java.util.function.Consumer;
class InputManager{
private Thread thread;
private InputManager(){}
private static InputManager instance;
private static InputManager getInstance(){
if(instance == null){
instance = new InputManager();
}
return instance;
}
/**
* Ask user to type a number.
* #param onSelected When the user has made his choice, this callback will be executed
*/
public static void ask( Consumer<Integer> onSelected){
getInstance().thread = new Thread(() -> {
System.out.println("Type a number:");
Scanner sc = new Scanner(System.in);
int selection = -1;
while (selection == -1) {
if(Thread.currentThread().isInterrupted()){
return;
}
if(sc.hasNextInt()){
selection = sc.nextInt();
onSelected.accept(selection);
} else {
sc.next();
selection = -1;
}
}
});
getInstance().thread.start();
}
/**
* Reset input stream (?)
*/
public static void closeInput(){
try {
getInstance().thread.interrupt();
} catch(NullPointerException e){
// do nothing
}
}
}
class Client {
/**
* Ask user to type a number and send it to the server
*/
void makeRequest(){
InputManager.closeInput();
InputManager.ask((selected) -> {
System.out.println("Sent message: " + selected);
});
}
public static void main(String[] args) {
Client client = new Client();
client.makeRequest();
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
System.out.println("Message received: thread interrupted");
client.makeRequest();
}
}, 5000, 5000);
}
}
As I see it, you can use 3 types of threads:
The main thread switches between users, announces players to play, checks the winning condition and starts the timer at each turn.
A second thread reads constantly the user input. After reading user input, it notifies the main thread.
Finally a thread waits for 5 seconds and then notifies the main thread.
So I will use 2 Producers and 1 Consumer as follows:
A Producer which "produces" the scanned user input (it provides it to the Consumer).
A Producer which "produces" time out events (which notify the Consumer).
A Consumer which switches between players and starts the producers.
All this, so that you don't have to mess around with interrupting any running thread and there is no need to check if the Scanner is ready…
import java.util.Scanner;
public class Main {
private static final Scanner SCAN = new Scanner(System.in);
//This is the Scanner's input Producer:
private static class UserInputProducer extends Thread {
private final UserInputConsumer uInConsumer;
public UserInputProducer(final UserInputConsumer uInConsumer) {
this.uInConsumer = uInConsumer;
}
#Override
public void run() {
while (true) {
final int input = SCAN.nextInt();
SCAN.nextLine(); //Ignore the new line character.
uInConsumer.userInput(input); //Fire user input event (for the current user).
}
}
}
//This is the time out event Producer:
private static class TimeOutEventProducer {
private final UserInputConsumer uInConsumer;
private int validReportId = Integer.MIN_VALUE; //IDs starting from Integer.MIN_VALUE and
//going step by step to Integer.MAX_VALUE, which means about 4 billion resets can be done
//to this Producer before an unhandled overflow occurs.
public TimeOutEventProducer(final UserInputConsumer uInConsumer) {
this.uInConsumer = uInConsumer;
}
public synchronized void reset() {
new TimerOnce(this, ++validReportId).start(); //Start a new TimerOnce. Could be javax.swing.Timer with "setRepeats(false)".
}
/*sleepDone(...) is called by ALL TimerOnce objects... So we need an up-to-date id (the
reportId) to verify that the LAST one TimerOnce finished, rather than any other.*/
public synchronized void sleepDone(final int reportId) {
if (reportId == validReportId) //Only the last one timeout is valid...
uInConsumer.timedOut(); //Fire time out event (for the current user).
}
}
//This is just a "Timer" object which blocks for 5 seconds:
private static class TimerOnce extends Thread {
private final TimeOutEventProducer timeout;
private final int reportId;
public TimerOnce(final TimeOutEventProducer timeout,
final int reportId) {
this.timeout = timeout;
this.reportId = reportId;
}
#Override
public void run() {
try { Thread.sleep(5000); } catch (final InterruptedException ie) {} //Wait.
timeout.sleepDone(reportId); //Report that the time elapsed...
}
}
//This is the Consumer:
private static class UserInputConsumer {
private final String[] names;
private int input;
private boolean timedOut, hasInput;
public UserInputConsumer(final String[] names) {
this.names = names;
}
public synchronized int play() {
new UserInputProducer(this).start(); //Start scanning any user's input...
final TimeOutEventProducer timeout = new TimeOutEventProducer(this);
int i = -1;
do {
i = (i + 1) % names.length;
hasInput = false;
timedOut = false;
timeout.reset(); //Start the input wait timer...
System.out.print("User " + names[i] + " enter a number: "); //Clarify who's player is the turn.
while (!hasInput && !timedOut)
try { wait(); } catch (final InterruptedException ie) {} //Wait for user input or timeout.
//Interpret notification event (either user input, either timeout):
if (timedOut)
System.out.println("Sorry, out of time.");
else if (!hasInput)
throw new UnsupportedOperationException("Probably messed with the flags in the while-condition.");
}
while (input != 5); //Here you test the win/loss condition.
//Lets say, for example, the user that enters number '5' wins...
return i; //Return the winner's index.
}
public synchronized void timedOut() {
timedOut = true;
notify();
}
public synchronized void userInput(final int input) {
this.input = input;
hasInput = true;
notify();
}
}
public static void main(final String[] args) {
System.out.print("Enter number of players: ");
final int numPlayers = SCAN.nextInt();
SCAN.nextLine(); //Ignore the new line character.
final String[] names = new String[numPlayers];
for (int i=0; i<names.length; ++i) {
System.out.print("User " + (i+1) + " enter your name: ");
names[i] = SCAN.nextLine();
}
//Start the consumer (which in turn starts the producers) and start the main logic:
System.out.println(names[new UserInputConsumer(names).play()] + " wins!");
}
}
Note, the program never terminates because the Scanning is infinite. But you may alter this behavior by messing with the while (true) condition of the UserInputProducer.
Alright, I worked out a solution.
As I thought, the problem is that the while loop was (of course) blocking in Scanner.hasNext. To avoid blocking, I used a BufferedReader, which has this handy function, ready, which returns true whenever a new line is input in System.in.
Basically, I changed the InputManager.ask method to:
void ask(Consumer<Integer> onSelected){
getInstance().thread = new Thread(() -> {
System.out.println("Type a number:");
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
Scanner sc = new Scanner(reader);
int selection = -1;
try {
while (selection == -1) {
//While there is no input to be processed
while (!reader.ready()) {
//This lets throw an InterruptedException
Thread.sleep(100);
}
if (sc.hasNextInt()) {
selection = sc.nextInt();
onSelected.accept(selection);
} else {
sc.next();
selection = -1;
}
}
} catch (IOException | InterruptedException e) {
// do nothing: function ends
}
});
getInstance().thread.start();
}
I also added this (extremely ugly) piece of code to consume any input before resetting, to prevent any previous line to be detected as typed now (basically flush the last line).
(If anyone has a suggestion on how this can be done in a more elegant way, I'm more than happy to hear your thoughs)
public static void closeInput(){
try {
BufferedReader tmp = new BufferedReader(new InputStreamReader(System.in));
if(tmp.ready()){
tmp.readLine();
}
getInstance().thread.interrupt();
} catch(NullPointerException e){
// do nothing
} catch (IOException e) {
e.printStackTrace();
}
}
I am executing a simple command where user gives the options(args) and I made a logic to get the args in Java program in which I am using wait() for particular time so that command will take that much minimum time to execute.I am saving some data in a file after that.
Within this time if the user wants to end the process ,should be able to stop the process smoothly by giving input like "exit" in the command prompt.
Please help.
The standard way of interrupting a command line program is by adding a Ctrl-C handler to your app:
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
// cleanup logic here
}
});
See this question for more details.
Since you insist. Here is an implementation when commands are executed in background threads. I hope the complexity of this example will deter you from implementing it:
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class Shell {
private static final int NUM_PARALLEL_COMMANDS = 5;
private static final int SLEEP_DURATION = 1000;
public static void main(String[] args) throws InterruptedException {
ExecutorService executor =
Executors.newFixedThreadPool(NUM_PARALLEL_COMMANDS);
try (Scanner scanner = new Scanner(System.in)) {
String command = null;
int counter = 0;
do {
command = scanner.nextLine();
switch (command) {
case "DoStuff":
executor.submit(NewDoStuffCommand(++counter));
break;
}
} while (!command.equals("exit"));
}
executor.shutdownNow();
executor.awaitTermination(1, TimeUnit.SECONDS);
}
private static Runnable NewDoStuffCommand(final int counter) {
return new Runnable() {
#Override
public void run() {
try {
for (int i = 0; i < 10; i++) {
System.out.println(counter + ": Doing time consuming things...");
Thread.sleep(SLEEP_DURATION);
}
System.out.println(counter + ": Finished.");
} catch (InterruptedException e) {
System.out.println(counter + ": Command interrupted :(");
// do cleanup
Thread.currentThread().interrupt();
}
}
};
}
}
I'm creating a program that prints the sound of three bells.
I need to stop the process when the user types a key on the keyboard, how can I do?
Also, I would make sure that every bell show your sound in a random time, making use of the property Math.random(). You can associate a random time to a thread?
package campane;
import java.io.*;
import java.util.*;
public class GestioneCampane extends Thread {
public static void main(String [] args) {
BufferedReader tast = new BufferedReader(new InputStreamReader(System.in));
char pressione;
Campane campana = new Campane("DIN", 300);
Campane campana2 = new Campane("DON", 1000);
Campane campana3 = new Campane("DAN", 2000);
campana.start();
campana2.start();
campana3.start();
}
}
this is the second class
package campane;
public class Campane extends Thread {
private String campane; // word to print
private int delay;
public Campane(String whatToSay, int delayTime) {
campane = whatToSay;
delay = delayTime;
}
public void run() {
try {
while(true) {
System.out.print(campane + " ");
Thread.sleep(delay);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
You can stop a thread by interrupting it:
Thread.interrupt()
You can stop a thread for a given time, and continue with it after the time is consumed:
Long time = 15000L; // in milliseconds
Thread.sleep(time);
Use KeyBindings to intercept keys pressed and randomize your sound relevence, this example can save time
I'm trying to write a loop which runs until I type a specific text in console where the application is running. Something like:
while (true) {
try {
System.out.println("Waiting for input...");
Thread.currentThread();
Thread.sleep(2000);
if (input_is_equal_to_STOP){ // if user type STOP in terminal
break;
}
} catch (InterruptedException ie) {
// If this thread was intrrupted by nother thread
}}
And I want it to write a line each time it pass through so I do not want it to stop within the while and wait for next input. Do I need to use multiple threads for this?
Do I need to use multiple threads for this?
Yes.
Since using a Scanner on System.in implies that you're doing blocking IO, one thread will need to be dedicated for the task of reading user input.
Here's a basic example to get you started (I encourage you to look into the java.util.concurrent package for doing these type of things though.):
import java.util.Scanner;
class Test implements Runnable {
volatile boolean keepRunning = true;
public void run() {
System.out.println("Starting to loop.");
while (keepRunning) {
System.out.println("Running loop...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
System.out.println("Done looping.");
}
public static void main(String[] args) {
Test test = new Test();
Thread t = new Thread(test);
t.start();
Scanner s = new Scanner(System.in);
while (!s.next().equals("stop"));
test.keepRunning = false;
t.interrupt(); // cancel current sleep.
}
}
Yes, you would need two threads for this. The first could do something like this:
//accessible from both threads
CountDownLatch latch = new CountDownLatch(1);
//...
while ( true ) {
System.out.println("Waiting for input...");
if ( latch.await(2, TimeUnit.SECONDS) ) {
break;
}
}
And the other:
Scanner scanner = new Scanner(System.in);
while ( !"STOP".equalsIgnoreCase(scanner.nextLine()) ) {
}
scanner.close();
latch.countDown();