Read from BufferedReader for a specific Duration

2019-08-10 12:34发布

问题:

So, I'm reading from a BufferedReader. Everything goes fine, until I add one condition. I need to read from BufferedReader for a specific duration of time.

This is what I'm doing right now.

while ((line = br.readLine()) != null
                    && System.currentTimeMillis() - start < maxReadTime.toMillis()) { 
    // doingSomethingHere()
}

The problem: InputStream is active even after the time has elapsed. For example - maxReadTime is 30 seconds. the input keeps coming in 20 seconds. For the next 12 seconds, there's no activity. Now when the next input arrives, the stream is open and closes only after it has read the input. However, I don't process this input because while loop terminates.

What I expected or what I need: is Stream will close at 30 seconds. That is when the input arrives at 32nd second, the stream is closed and not listening to any input.

I know vaguely about ExecutorService. I'm not sure if that's the correct way to go.

回答1:

Just put your timer condition before reading from stream

while ((line = br.readLine()) != null) {
    boolean active = System.currentTimeMillis() - start < maxReadTime.toMillis();
    if (!active) {
        br.close();
    }         
    // doingSomethingHere()
}

In this case if the first condition is false (time is expired), the second won't be executed at all



回答2:

Basically you have to check if buffer is ready before call readLine() by calling method ready(), for InputStream check available() method which returns how may bytes you can read without block.

Here an example

import java.io.*;
import java.time.Duration;

public class Main {

    public static void main(String[] args) {
        final InputStream in =  System.in; //new FileInputStream(new File("/tmp/x"));
        final String out = readInput(in, Duration.ofSeconds(5));
        System.out.printf("m=main, status=complete, out=%s%n", out);
    }

    public static String readInput(InputStream in, Duration duration) {
        final long timestamp = System.currentTimeMillis();
        final BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        final StringBuilder out = new StringBuilder();
        try {
            String line = null;
            while (true){
                if(Duration.ofMillis(System.currentTimeMillis() - timestamp).compareTo(duration) >=0 ){
                    System.out.println("m=readInput, status=timeout");
                    break;
                }
                if(!reader.ready()){
                    System.out.println("m=readInput, status=not ready");
                    sleep(1000);
                    continue;
                }
                line = reader.readLine();
                if(line == null){
                    System.out.println("m=readInput, status=null line");
                    break;
                }
                out.append(line);
                out.append('\n');
                System.out.printf("m=readInput status=read, line=%s%n" , line);
            }
            return out.toString();
        } catch (IOException e){
            throw new RuntimeException(e);
        } finally {
            System.out.println("m=readInput, status=complete");
        }
    }

    static void sleep(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {}
    }

}

If you wanna do this in background you can follow this example

package com.mageddo;

import java.io.*;
import java.util.concurrent.*;

public class Main {

public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
        final InputStream in =  System.in; //new FileInputStream(new File("/tmp/x"));
        final StringBuilder out = new StringBuilder();
        final ExecutorService executor = Executors.newFixedThreadPool(1);
        final Future<String> promise = executor.submit(() -> readInput(in, out));
        try {
            final String result = promise.get(5, TimeUnit.SECONDS);
            System.out.printf("m=main, status=success, result=%s%n", result);
        } catch (TimeoutException e) {
            System.out.println("m=main, status=timeout");
            in.close();
            promise.cancel(true);
            System.out.println("Failed output: " + promise.get());
            e.printStackTrace();
        } finally {
            executor.shutdown();
            System.out.println("m=main, status=shutdown, out=" + out);
        }
    }

    public static String readInput(InputStream in, StringBuilder out) {
        final BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        try {
            String line = null;
            while (true){
                if(Thread.currentThread().isInterrupted()){
                    System.out.println("m=readInput status=interrupt signal");
                    break;
                }
                if(!reader.ready()){
                    System.out.println("m=readInput, status=not ready");
                    sleep(1000);
                    continue;
                }
                line = reader.readLine();
                if(line == null){
                    System.out.println("m=readInput, status=null line");
                    break;
                }
                out.append(line);
                out.append('\n');
                System.out.printf("m=readInput status=read, line=%s%n" , line);
            }
            return out.toString();
        } catch (IOException e){
            throw new RuntimeException(e);
        } finally {
            System.out.println("m=readInput, status=complete");
        }
    }

    static void sleep(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

}

See the reference