Interrupt BufferedReader#readLine() without closing InputStream

前端 未结 2 704
长发绾君心
长发绾君心 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: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

提交回复
热议问题