Interrupt BufferedReader#readLine() without closing InputStream

前端 未结 2 665
长发绾君心
长发绾君心 2021-01-11 16:43

The InputStream of my Process should attach and detach whenever the user wants to see it or not. The attaching works fine, but the detach fails. De

相关标签:
2条回答
  • 2021-01-11 17:14

    You're going at this back to front.

    You can't stop collecting the process's output, or you will stall the child process.

    You want to stop displaying the output when the user doesn't want to see it. Look on it as a user interface issue only.

    0 讨论(0)
  • 2021-01-11 17:22

    I didn't expect it to work, but futures are actually cancelable (but why?). After @Tarun Lalwani mentioned the TimeLimiter of Googles Guava library, I inspected the code, tried it in my examples (worked!) and rewrote it a bit - make it not time-based, but method-call-based?!

    Here is what I got from my research: A wrapper for the BufferedReader:

    public class CancelableReader extends BufferedReader {
    
        private final ExecutorService executor;
        private Future future;
    
        public CancelableReader(Reader in) {
            super(in);
            executor = Executors.newSingleThreadExecutor();
        }
    
        @Override
        public String readLine() {
    
            future = executor.submit(super::readLine);
    
            try {
                return (String) future.get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            } catch (CancellationException e) {
                return null;
            }
    
            return null;
    
        }
    
        public void cancelRead() {
            future.cancel(true);
        }
    
    }
    

    This class allows you to use the BufferedReader#readLine() when you need it and cancel it when you want to continue / interrupt the Thread it is running in. Here is some example code of it in action:

    public static void main(String[] args) {
    
        System.out.println("START");
    
        CancelableReader reader = new CancelableReader(new InputStreamReader(System.in));
        String line;
    
        new Thread(() -> {
    
            try {
    
                Thread.sleep(10000);
                reader.cancelRead();
    
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
        }).start();
    
        while ((line = reader.readLine()) != null) {
            System.out.println(line);
        }
    
        System.out.println("END");
    
    }
    

    And the output of it:

    START
    > Hello World!
    Hello World!
    > What's up?
    What's up?
    END //Exactly after 5 seconds, when the cancel was called
    > Hey, you still there?
    //No output as expected
    

    And the last thing I wanna say is why this and not closing InputStream or create one Thread per process? In this case the InputStream is the stream of a Process, which means we cannot close it. One way would be to unblock readLine() and return null to finish the while-loop, but this is made with Reflection, which is not as beautiful as our solution now and didn't work for any reason. The application uses many processes but has a limited amount of users - thats why we decide for the amount of threads per user and not per process.

    I hope you guys will find this Thread in the future and it is helpful for you. Would be awesome if you leave an upvote, so I can get back my rep of the bounty. Dont forget to upvote the comments either! They helped me alot and brought me to the right solution: Interrupt BufferedReader#readLine() without closing InputStream

    0 讨论(0)
提交回复
热议问题