Stop Scanner from waiting for input - java

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();
}
}

Related

Why Lock is not pass to another thread

When buffer queue is empty Thread is waiting for Another thread to enter element in buffer queue after it notifies the first thread to activate and run the remaining code but only one thread which is taking input in buffer queue is active and another thread is not working. I think thread notify is not working.
Main.java
public class Main {
public static void main(String[] args) throws InterruptedException {
LRUManager lruManager = new LRUManager();
Thread input = new Thread(lruManager);
Thread lruCache = new Thread(lruManager);
input.setName("Input");
lruCache.setName("LRUCache");
input.start();
lruCache.start();
}
}
LRUManager.java
public class LRUManager implements Runnable {
LRUCache lruCache = new LRUCache();
Input input = new Input();
#Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println("ThreadName:" + threadName);
if (threadName.equalsIgnoreCase("Input")) {
inputController();
} else if (threadName.equalsIgnoreCase("LRUCache")) {
lruController();
}
}
synchronized private void lruController() {
if (input.checkbufferQueue()) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
int page = input.getinput();
lruCache.addPage(page);
lruCache.display();
run();
}
}
synchronized private void inputController() {
input.takeInput();
input.printQueue();
notifyAll();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
run();
}
}
Input.java
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class Input {
Scanner scanner = new Scanner(System.in);
int page;
Queue<Integer> bufferQueue = new LinkedList<Integer>();
void takeInput() {
System.out.println("Please enter Page in the cache");
page = scanner.nextInt();
bufferQueue.add(page);
}
int getinput() {
int page = bufferQueue.poll();
return page;
}
boolean checkbufferQueue()
{
return bufferQueue.isEmpty();
}
void printQueue() {
System.out.print("bufferQueue: ");
for(int page: bufferQueue) {
System.out.print(page+" ");
}
System.out.println("\n");
}
}
LRUCache.java
import java.util.Deque;
import java.util.Iterator;
import java.util.LinkedList;
public class LRUCache {
static Deque<Integer> cache = new LinkedList<Integer>();
Input input = new Input();
private int cacheSize = 4;
int page;
void addPage(int page) {
if (!cache.contains(page)) {
if (cache.size() == cacheSize) {
cache.removeLast();
}
} else {
cache.remove(page);
}
cache.push(page);
}
public void display() {
Iterator<Integer> itr = cache.iterator();
while (itr.hasNext()) {
System.out.print(itr.next() + " ");
}
System.out.print("\n");
}
}
output
ThreadName:LRUCache
ThreadName:Input
Please enter Page in the cache
1
bufferQueue: 1
ThreadName:Input
Please enter Page in the cache
2
bufferQueue: 1 2
ThreadName:Input
Please enter Page in the cache
3
bufferQueue: 1 2 3
ThreadName:Input
Please enter Page in the cache
4
bufferQueue: 1 2 3 4
ThreadName:Input
Please enter Page in the cache
5
bufferQueue: 1 2 3 4 5
ThreadName:Input
Please enter Page in the cache
Your input thread is never exiting method synchronization, hence never releasing the lock. Notify tells the LRU thread to move out of waiting state, but it then the thread is then left blocked waiting for the lock that never arrives.
What you need is a wait() after the notifyAll() in inputController(), and a notifyAll() before wait in the lruController().
Refer to https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html for examples.
Also make the recursive call from lruController() outside the else block.
Could you also change the recursive calls within the thread to call inputController() or lruController() instead of run directly.

How to make 2 threads share data? (Java)

I'm currently practicing Threads so I tasked myself to write a program that will create 2 threads. The first one will endlessly print a character and the second one will endlessly wait for input and then pass it to the first Thread. Then Thread #1 should print the passed character. Here's what I wrote:
public class A extends Thread {
public char dif;
Scanner stdin = new Scanner(System.in);
#Override
public void run() {
for (; ; ) {
dif = stdin.nextLine().charAt(0);
MyThread.setCh(dif);
}
}
}
This thread takes input and then passes it to this one:
public class MyThread extends Thread {
public static char ch;
public static void setCh(char cha) {
ch = cha;
}
public static char getCh() {
return ch;
}
#Override
public void run() {
for(;;) {
try {
Thread.sleep(300);
}
catch(InterruptedException e) {
e.printStackTrace();
}
System.out.print(getCh());
}
}
}
And what happens in the main():
MyThread endless = new MyThread();
MyThread.setCh('$');
A set = new A();
endless.start();
set.start();
However, this doesn't work as intended. No matter what I type, the program keeps printing $. Also for some reason the first time I type a character I get an Out of bounds exception.
Probably, the easiest way to approach this, is to use BlockingQueue.
Effectively, in your example the thread, that receives character from System.in is producer and the thread that prints received character is consumer.
So, here is the code that achieves your goal:
import java.util.*;
import java.util.concurrent.*;
class Setup {
public static void main(String[] args) {
BlockingQueue<Character> q = new LinkedBlockingQueue<>();
Producer p = new Producer(q);
Consumer c = new Consumer(q);
new Thread(p).start();
new Thread(c).start();
}
}
class Producer implements Runnable {
private final BlockingQueue<Character> queue;
private final Scanner scanner = new Scanner(System.in);
Producer(BlockingQueue<Character> q) { queue = q; }
public void run() {
try {
while (true) {
queue.put(produce());
}
} catch (InterruptedException ex) {
Thread.currentThread().interrupt(); // set interrupt flag
} finally {
scanner.close();
}
}
Character produce() {
return scanner.nextLine().charAt(0);
}
}
class Consumer implements Runnable {
private final BlockingQueue<Character> queue;
Consumer(BlockingQueue<Character> q) { queue = q; }
public void run() {
try {
while (true) {
consume(queue.take());
}
} catch (InterruptedException ex) {}
}
void consume(Character c) {
System.out.println("Received character: " + c);
}
}
The problem actually you have it is quite small delay in Thread.sleep(300);
Try to set sleep for a few seconds Thread.sleep(5000);. You have to type something before it will print previous char

java, creating a loop while keyboardscanner dont receive an input

I´m trying to make a loop while keyboardscanner.nextline() is not received.I am stuck in here because i cant find a solution, i dont even know if it is possible... here is what im trying to do in code...
public String messagingread(String username) throws RemoteException {
Scanner keyboardScanner = new Scanner(System.in);
try {
while (keyboardScanner.nextLine().isEmpty) {
System.out.println("cant get in here");
//i can only get in here if the scann is only an enter(isempty), but i //want to get in here if i dont scan anything...i dont want isempty i want not //defined and i dont know how to do it ....
}
System.out.println("pls help")
}
}
this will execute a task in other thread and accept inputs till the input is empty. Also, be aware of closing Scanner
class Task implements Runnable {
private boolean shouldRun = true;
public void stop() {
this.shouldRun = false;
}
#Override
public void run() {
while (this.shouldRun) {
try {
Thread.sleep(1000);
System.out.println("Doing some work every 1 second ...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Task have been stopped, Bye!");
Thread.currentThread().interrupt();
}
}
public final class Example {
public static void main(String[] args) {
Scanner keyboardScanner = new Scanner(System.in);
try {
Task task = new Task();
// run the task on new Thread
Thread newThread = new Thread(task);
newThread.start();
/*
read lines while it is not empty:
(line = keyboardScanner.nextLine()) -> assign the input to line
!(line ...).isEmpty() -> checks that line is not empty
*/
System.out.println("Give me inputs");
String line;
while (!(line = keyboardScanner.nextLine()).isEmpty()) {
System.out.println("new line read :" + line);
}
// when you give an empty line the while will stop then we stop
// the task
task.stop();
} finally {
// after the piece of code inside the try statement have finished
keyboardScanner.close();
}
System.out.println("Empty line read. Bye!");
}
}
// retrieve not empty line
public static String messagingread(String username) {
try (Scanner scan = new Scanner(System.in)) {
while (true) {
String line = scan.nextLine();
// do it while line is empty
if (!line.isEmpty())
return line;
}
}
}

How to intercept a keypress in a Java application?

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

Process.getOutputStream.close() doesn't seem to work or something else?

I have this problem on my project for 2days now.. And I'm not sure what is the fault. My project includes 2 classes, the main and another one to be called via ProcessBuilder using ExecutorService.
Here are the codes.
main class:
public class DesktopApplication1View extends FrameView{
public Process process = null;
private executeCode execute = new executeCode();
public DesktopApplication1View(SingleFrameApplication app){
(.........)
jButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
console.setText("");
execute.start();
}
});
}
class executeCode implements Runnable{
private boolean executeStarted = false;
private ExecutorService executecode;
ReadStdout read;
WriteStdin write;
public executeCode(){
executecode = Executors.newSingleThreadExecutor();
read = new ReadStdout();
write = new WriteStdin();
}
public void start(){
if(executeStarted){
try {
// process.getInputStream().close();
process.getOutputStream().close();
process.destroy();
} catch (IOException ex) {}
}
console.append("start\n");//debugging purpose
executecode.execute(this);
}
public void run(){
console.append("Execute thread = " + Thread.currentThread().getName() + "\n");//debugging purpose
executeStarted = true;
try {
ProcessBuilder builder = new ProcessBuilder("java", "-cp", "Project.jar", "project/oddeven");
builder.redirectErrorStream(true);
process = builder.start();
read.startReadStdout();
write.startWriteStdin();
}
catch (IOException e1) {console.append("error io");}
// return;
}
}
class WriteStdin implements Runnable{
private String input = null;
private BufferedWriter writer = null;
private ExecutorService executeWrite = Executors.newSingleThreadExecutor();
public void startWriteStdin(){
writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
executeWrite.execute(this);
}
public void WriteStdin(){
console.addKeyListener(new java.awt.event.KeyAdapter() {
#Override
public void keyTyped(java.awt.event.KeyEvent e){
//save the last lines for console to variable input
if(e.getKeyChar() == '\n'){
try {
int line = console.getLineCount() -2;
int start = console.getLineStartOffset(line);
int end = console.getLineEndOffset(line);
input = console.getText(start, end - start);
writer.write(input);
writer.flush();
} catch (Exception e1) {}
}
}
});
}
#Override
public void run(){
console.append("Write thread = " + Thread.currentThread().getName() + "\n");//debugging purpose
if(input == null) this.WriteStdin();
}
}
class ReadStdout implements Runnable{
private ExecutorService executeRead = Executors.newSingleThreadExecutor();
private BufferedReader reader = null;
public void startReadStdout(){
reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
executeRead.execute(this);
}
public void run() {
console.append("Read thread = " + String.valueOf(Thread.currentThread().getName()) + "\n");//debugging purpose
String line;
try {
while((line = reader.readLine())!=null)
console.append(line+"\n");
}catch (IOException e) {}
console.append("read done");//debugging purpose
}
}
oddeven class:
public class oddeven{
static double num = 0;
static int even = 0, odd = 0, zero = 0;
public static void main(String[]args) throws Exception{
odd();
}
public static void odd() throws Exception{
try{
BufferedReader dataIn = new BufferedReader(new InputStreamReader(System.in));
System.out.print("Enter numbers\n(Input negative value to end)\n");
num = Double.parseDouble(dataIn.readLine());
while(num>=0){
if(num == 0)
zero++;
else if(num%2==0)
even++;
else
odd++;
num = Double.parseDouble(dataIn.readLine());
}
System.out.print("There are\n"+even+" even numbers\n"+odd+" odd numbers\n"+zero+" zero value");
} catch(NumberFormatException e){
System.out.print("Wrong input, enter again\n");
odd();
}
}
}
So. If I clicked the button once, output is:
start
Execute thread = pool-2-thread-1
Read thread = pool-3-thread-1
Write thread = pool-4-thread-1
Enter numbers
(Input negative value to end)
1
2
-1
There are
1 even numbers
1 odd numbers
0 zero value
read done
but if I close then start the application again then click the button twice without inputting anything on the first click, output will be:
start
Execute thread = pool-2-thread-1
read doneRead thread = pool-3-thread-1
Write thread = pool-4-thread-1
Enter numbers
(Input negative value to end)
1
2
-1
There are
2 even numbers
2 odd numbers
0 zero value
read done
As you can see, when I click the button twice without inputting on the first click, the outputstream is not closed, which I think is causing the oddeven class registers 2 input from the stream. But if you input on the first click then 2nd click didn't get any input and you clicked the button again, the stream won't register 2 input anymore, it will only register one.
This shouldn't happen, because firstly, the process is destroyed if you click the button and the bufferedwriter is instanced again. Or I can be wrong.
I hope you get my point. Thank you.

Categories

Resources