javamultithreadinginputiobufferedreader

Reading from System.in in multiple threads


I have the following problem:

  1. method1 that keeps reading from System.in and sending it via Socket
  2. method2 that only reads from System.in when a error occurs in it

When I have a problem in method2, I have to press ENTER twice, because, even it closing the socket, the readLine() on method1 was already executed, so it first read the line for method1, then it takes the input for method2.

Edit: The method1 is constantly receiving messages from the socket and method2 is sending everything I type on console via Socket. But when the receive of messages fail, I want to just ask if the client wants to connect to other server, but the method1 already read the line, so the first time i type goes to the now useless method1, then the second line goes correctly to method2

What can I do in this case?

Observation: I know in the example I can make the reader as a class property. Just made this way to make it easier to understand

public class App {

    public void method1() {
        new Thread(() -> {
            String line;
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            while (!socket.isClosed() && line = bufferedReader.readLine() != null) {
                //Keeps reading from the System.in and sending to the Socket
            }
        }).start();
    }

    public void method2() {
        try {
            //Keeps receiving messages from the Socket
        } catch (Exception e) {
            socket.close();
            String line;
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            line = bufferedReader.readLine();
            //Asks if the user want to connect to other server
        }
    }
}

Solution

  • There is no easy way to do that because System.in is a bare InputStream, which is uninterruptible, unstoppable, and will block the caller until it is closed or EOF is reached. One ugly solution is to create a new thread and stop the old thread. However, this requires using the deprecated Thread.stop() method.

    public class App {
        private Thread sender;
    
        private void stopSender() {
            if (sender != null) {
                sender.stop();
                sender = null;
            }
       }
    
        private void startSender() {
            // stop previous sender if necessary
            stopSender();
            sender = new Thread(() -> String line;
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
                while (!socket.isClosed() && line = bufferedReader.readLine() != null) {
                    //Keeps reading from the System.in and sending to the Socket
                }
            });
            sender.start();
        }
    
        public void method2() {
            try {
                //Keeps receiving messages from the Socket
            } catch (Exception e) {
                // stop sender first
                stopSender();
                socket.close();
                String line;
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
                line = bufferedReader.readLine();
                //Asks if the user want to connect to other server
                // start a new sender
                startSender();
            }
    }
    
    

    Alternatively, there is a more elegant but complex solution: use non-blocking I/O. However, Java doesn't support selectable System.in directly, so you would need to write JNI/JNA code.

    Anyway, there is no easy solution to this.