Có thể đọc từ InputStream với thời gian chờ không?


147

Cụ thể, vấn đề là viết một phương thức như thế này:

int maybeRead(InputStream in, long timeout)

trong đó giá trị trả về giống như in.read () nếu dữ liệu khả dụng trong 'thời gian chờ' mili giây và -2 nếu không. Trước khi phương thức trả về, bất kỳ luồng sinh sản nào cũng phải thoát.

Để tránh các đối số, chủ đề ở đây java.io.InputStream, như được tài liệu bởi Sun (bất kỳ phiên bản Java nào). Xin lưu ý điều này không đơn giản như vẻ ngoài của nó. Dưới đây là một số sự kiện được hỗ trợ trực tiếp bởi tài liệu của Sun.

  1. Phương thức in.read () có thể không bị gián đoạn.

  2. Việc gói InputStream trong Reader hoặc InterruptibleChannel không giúp ích gì, bởi vì tất cả các lớp đó có thể làm là các phương thức gọi của InputStream. Nếu có thể sử dụng các lớp đó, có thể viết một giải pháp chỉ thực hiện cùng một logic trực tiếp trên InputStream.

  3. Luôn luôn chấp nhận được khi in.av Available () trả về 0.

  4. Phương thức in.close () có thể chặn hoặc không làm gì cả.

  5. Không có cách chung để giết một chủ đề khác.

Câu trả lời:


83

Sử dụng inputStream.av Available ()

System.in.av Available () luôn được chấp nhận để trả về 0.

Tôi đã tìm thấy điều ngược lại - nó luôn trả về giá trị tốt nhất cho số byte có sẵn. Javadoc cho InputStream.available():

Returns an estimate of the number of bytes that can be read (or skipped over) 
from this input stream without blocking by the next invocation of a method for 
this input stream.

Một ước tính là không thể tránh khỏi do thời gian / độ cứng. Con số có thể được đánh giá thấp một lần vì dữ liệu mới liên tục đến. Tuy nhiên, nó luôn "bắt kịp" trong cuộc gọi tiếp theo - nó sẽ chiếm tất cả dữ liệu đã đến, thanh đến ngay tại thời điểm của cuộc gọi mới. Trả về 0 vĩnh viễn khi có dữ liệu không thành công với điều kiện trên.

Thông báo đầu tiên: Các lớp con cụ thể của InputStream chịu trách nhiệm về tính khả dụng ()

InputStreamlà một lớp trừu tượng. Nó không có nguồn dữ liệu. Nó là vô nghĩa đối với nó để có dữ liệu có sẵn. Do đó, javadoc available()cũng cho biết:

The available method for class InputStream always returns 0.

This method should be overridden by subclasses.

Và thực tế, các lớp luồng đầu vào cụ thể ghi đè có sẵn (), cung cấp các giá trị có ý nghĩa, không phải là 0 không đổi.

Thông báo thứ hai: Đảm bảo bạn sử dụng trả lại vận chuyển khi nhập dữ liệu vào Windows.

Nếu sử dụng System.in, chương trình của bạn chỉ nhận được đầu vào khi shell lệnh của bạn chuyển giao nó. Nếu bạn đang sử dụng chuyển hướng / đường dẫn tệp (ví dụ: somefile> java myJavaApp hoặc somecommand | java myJavaApp), thì dữ liệu đầu vào thường được chuyển giao ngay lập tức. Tuy nhiên, nếu bạn nhập thủ công, thì việc chuyển dữ liệu có thể bị trì hoãn. Ví dụ: Với shell cmd.exe, dữ liệu được đệm trong shell cmd.exe. Dữ liệu chỉ được chuyển đến chương trình java thực thi sau khi trả về vận chuyển (control-m hoặc <enter>). Đó là một hạn chế của môi trường thực thi. Tất nhiên, InputStream.av Available () sẽ trả về 0 miễn là trình bao đệm dữ liệu - đó là hành vi đúng; không có dữ liệu có sẵn tại thời điểm đó. Ngay khi dữ liệu có sẵn từ shell, phương thức trả về giá trị> 0. NB: Cygwin sử dụng cmd.

Giải pháp đơn giản nhất (không chặn, do đó không cần thời gian chờ)

Chỉ cần sử dụng này:

    byte[] inputData = new byte[1024];
    int result = is.read(inputData, 0, is.available());  
    // result will indicate number of bytes read; -1 for EOF with no data read.

HOẶC tương đương,

    BufferedReader br = new BufferedReader(new InputStreamReader(System.in, Charset.forName("ISO-8859-1")),1024);
    // ...
         // inside some iteration / processing logic:
         if (br.ready()) {
             int readCount = br.read(inputData, bufferOffset, inputData.length-bufferOffset);
         }

Giải pháp phong phú hơn (lấp đầy tối đa bộ đệm trong khoảng thời gian chờ)

Khai báo điều này:

public static int readInputStreamWithTimeout(InputStream is, byte[] b, int timeoutMillis)
     throws IOException  {
     int bufferOffset = 0;
     long maxTimeMillis = System.currentTimeMillis() + timeoutMillis;
     while (System.currentTimeMillis() < maxTimeMillis && bufferOffset < b.length) {
         int readLength = java.lang.Math.min(is.available(),b.length-bufferOffset);
         // can alternatively use bufferedReader, guarded by isReady():
         int readResult = is.read(b, bufferOffset, readLength);
         if (readResult == -1) break;
         bufferOffset += readResult;
     }
     return bufferOffset;
 }

Sau đó sử dụng:

    byte[] inputData = new byte[1024];
    int readCount = readInputStreamWithTimeout(System.in, inputData, 6000);  // 6 second timeout
    // readCount will indicate number of bytes read; -1 for EOF with no data read.

1
Nếu is.available() > 1024đề nghị này sẽ thất bại. Có những luồng chắc chắn trả về 0. SSLSockets chẳng hạn cho đến gần đây. Bạn không thể dựa vào điều này.
Hầu tước Lorne

Trường hợp 'is.av Available ()> 1024' được xử lý cụ thể thông qua readLpm.
Glen tốt nhất

Nhận xét lại SSLSockets không chính xác - nó trả về 0 nếu không có dữ liệu trong bộ đệm. Theo câu trả lời của tôi. Javadoc: "Nếu không có byte được đệm trên ổ cắm và ổ cắm chưa được đóng bằng cách sử dụng đóng, thì khả dụng sẽ trả về 0."
Glen Best

@GlenBest Nhận xét của tôi lại SSLSocket không sai. Cho đến gần đây [sự nhấn mạnh của tôi] nó thường trở về số 0 mọi lúc. Bạn đang nói về hiện tại. Tôi đang nói về toàn bộ lịch sử của JSSE và tôi đã làm việc với nó trước khi nó lần đầu tiên được đưa vào Java 1.4 vào năm 2002 .
Hầu tước Lorne

Bằng cách thay đổi các điều kiện vòng lặp while thành "while (is.av Available ()> 0 && System.civerseTimeMillis () <maxTimeMillis && buffer Offerset <b.length) {" đã tiết kiệm cho tôi một tấn chi phí CPU.
Logic1

65

Giả sử luồng của bạn không được hỗ trợ bởi một ổ cắm (vì vậy bạn không thể sử dụng Socket.setSoTimeout()), tôi nghĩ cách tiêu chuẩn để giải quyết loại vấn đề này là sử dụng Tương lai.

Giả sử tôi có trình thực thi và luồng sau:

    ExecutorService executor = Executors.newFixedThreadPool(2);
    final PipedOutputStream outputStream = new PipedOutputStream();
    final PipedInputStream inputStream = new PipedInputStream(outputStream);

Tôi có nhà văn viết một số dữ liệu sau đó đợi trong 5 giây trước khi viết đoạn dữ liệu cuối cùng và đóng luồng:

    Runnable writeTask = new Runnable() {
        @Override
        public void run() {
            try {
                outputStream.write(1);
                outputStream.write(2);
                Thread.sleep(5000);
                outputStream.write(3);
                outputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };
    executor.submit(writeTask);

Cách đọc bình thường này là như sau. Việc đọc sẽ chặn vô thời hạn đối với dữ liệu và do đó, việc này sẽ hoàn thành sau 5 giây:

    long start = currentTimeMillis();
    int readByte = 1;
    // Read data without timeout
    while (readByte >= 0) {
        readByte = inputStream.read();
        if (readByte >= 0)
            System.out.println("Read: " + readByte);
    }
    System.out.println("Complete in " + (currentTimeMillis() - start) + "ms");

đầu ra nào:

Read: 1
Read: 2
Read: 3
Complete in 5001ms

Nếu có một vấn đề cơ bản hơn, như nhà văn không trả lời, người đọc sẽ chặn mãi mãi. Nếu tôi kết thúc việc đọc trong tương lai, thì tôi có thể kiểm soát thời gian chờ như sau:

    int readByte = 1;
    // Read data with timeout
    Callable<Integer> readTask = new Callable<Integer>() {
        @Override
        public Integer call() throws Exception {
            return inputStream.read();
        }
    };
    while (readByte >= 0) {
        Future<Integer> future = executor.submit(readTask);
        readByte = future.get(1000, TimeUnit.MILLISECONDS);
        if (readByte >= 0)
            System.out.println("Read: " + readByte);
    }

đầu ra nào:

Read: 1
Read: 2
Exception in thread "main" java.util.concurrent.TimeoutException
    at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:228)
    at java.util.concurrent.FutureTask.get(FutureTask.java:91)
    at test.InputStreamWithTimeoutTest.main(InputStreamWithTimeoutTest.java:74)

Tôi có thể bắt TimeoutException và làm bất cứ việc gì tôi muốn.


14
Nhưng còn chủ đề chặn thì sao?! Nó sẽ ở lại trong bộ nhớ cho đến khi ứng dụng kết thúc? Nếu tôi đúng, điều này có thể tạo ra các luồng vô tận, ứng dụng bị tải nặng và thậm chí nhiều hơn, chặn các luồng tiếp theo sử dụng nhóm của bạn có các luồng bị chiếm và chặn. Vui long sửa cho tôi nêu tôi sai. Cảm ơn bạn.
Muhammad Gelbana

4
Muhammad Gelbana, bạn đã đúng: chuỗi lệnh read () vẫn chạy và điều đó không ổn. Tôi đã tìm ra cách để ngăn chặn điều này: khi hết thời gian chờ, hãy đóng luồng xử lý cuộc gọi (trong trường hợp của tôi, tôi đóng ổ cắm bluetooth android từ đó luồng đầu vào xuất hiện). Khi bạn làm điều đó, cuộc gọi read () sẽ trả về ngay lập tức .. Trong trường hợp của tôi, tôi sử dụng quá tải int read (byte []) và cuộc gọi đó sẽ trả về ngay lập tức. Có lẽ quá tải int read () sẽ ném IOException vì tôi không biết nó sẽ trả về cái gì ... Theo tôi đó là giải pháp thích hợp.
Emmanuel Touzery

5
-1 khi các luồng đọc bị chặn cho đến khi ứng dụng kết thúc.
Ortwin Angermeier

11
@ortang Đó là những gì tôi muốn nói là "bắt TimeoutException và làm bất cứ việc gì dọn dẹp ..." Ví dụ tôi có thể muốn giết luồng đọc: ... Catch (TimeoutException e) {execor.shutdownNow (); }
Ian Jones

12
executer.shutdownNowsẽ không giết được chủ đề. Nó sẽ cố gắng làm gián đoạn nó, không có hiệu lực. Không có khả năng dọn dẹp và đây là một vấn đề nghiêm trọng.
Marko Topolnik

22

Nếu InputStream của bạn được hỗ trợ bởi một Ổ cắm, bạn có thể đặt thời gian chờ của Ổ cắm (tính bằng mili giây) bằng setSoTimeout . Nếu cuộc gọi read () không bỏ chặn trong khoảng thời gian chờ được chỉ định, nó sẽ đưa ra một SocketTimeoutException.

Chỉ cần đảm bảo rằng bạn gọi setSoTimeout trên Ổ cắm trước khi thực hiện cuộc gọi read ().


18

Tôi sẽ đặt câu hỏi về tuyên bố vấn đề thay vì chỉ chấp nhận nó một cách mù quáng. Bạn chỉ cần thời gian chờ từ bảng điều khiển hoặc qua mạng. Nếu cái sau bạn có Socket.setSoTimeout()HttpURLConnection.setReadTimeout()cả hai đều làm chính xác những gì được yêu cầu, miễn là bạn thiết lập chúng chính xác khi bạn xây dựng / mua chúng. Để nó đến một điểm tùy ý sau này trong ứng dụng khi tất cả những gì bạn có là InputStream có thiết kế kém dẫn đến việc triển khai rất khó xử.


10
Có những tình huống khác trong đó một lần đọc có thể có khả năng chặn trong một thời gian đáng kể; ví dụ: khi đọc từ ổ đĩa băng, từ ổ đĩa mạng được gắn từ xa hoặc từ HFS với rô bốt băng ở đầu sau. (Nhưng lực đẩy chính của câu trả lời của bạn là đúng.)
Stephen C

1
@StephenC +1 cho nhận xét và ví dụ của bạn. Để thêm ví dụ của bạn, một trường hợp đơn giản có thể là nơi các kết nối ổ cắm được thực hiện chính xác nhưng nỗ lực đọc đã bị chặn vì dữ liệu được lấy từ DB nhưng bằng cách nào đó đã không xảy ra (giả sử DB không phản hồi và truy vấn đã đi ở trạng thái Khóa). Trong kịch bản này, bạn cần có cách hết thời gian chờ thao tác đọc trên ổ cắm.
sactiw

1
Toàn bộ quan điểm của sự trừu tượng InputStream là không nghĩ về việc triển khai cơ bản. Thật công bằng khi tranh luận về những ưu và nhược điểm của các câu trả lời được đăng. Nhưng, để đặt câu hỏi về tuyên bố vấn đề, sẽ không giúp ích gì cho sự xáo trộn
pellucide

2
InputStream hoạt động trên một luồng và nó chặn, nhưng nó không cung cấp cơ chế hết thời gian chờ. Vì vậy, trừu tượng InputStream không phải là một trừu tượng được thiết kế thông minh. Do đó, yêu cầu cách hết thời gian trên luồng không yêu cầu nhiều. Vì vậy, câu hỏi là yêu cầu một giải pháp cho một vấn đề rất thực tế. Hầu hết các triển khai cơ bản sẽ chặn. Đó chính là bản chất của một dòng. Ổ cắm, Tệp, Đường ống sẽ chặn nếu phía bên kia của luồng không sẵn sàng với dữ liệu mới.
nén

2
@EJP. Tôi không biết làm thế nào bạn có được điều đó. Tôi đã không đồng ý với bạn. Báo cáo sự cố "cách hết thời gian trên InputStream" là hợp lệ. Vì khung không cung cấp cách hết thời gian, nên thích hợp để hỏi một câu hỏi như vậy.
nang

7

Tôi chưa sử dụng các lớp từ gói Java NIO, nhưng có vẻ như chúng có thể giúp ích ở đây. Cụ thể, java.nio.channels.Channelsjava.nio.channels.InterruptibleChannel .


2
+1: Tôi không tin rằng có một cách đáng tin cậy để thực hiện những gì OP yêu cầu chỉ với InputStream. Tuy nhiên, nio đã được tạo ra cho mục đích này, trong số những người khác.
Eddie

2
OP về cơ bản đã loại trừ điều này. InputStreams vốn đã bị chặn và có thể không bị gián đoạn.
Hầu tước Lorne

5

Đây là một cách để lấy NIO FileChannel từ System.in và kiểm tra tính khả dụng của dữ liệu bằng cách sử dụng thời gian chờ, đây là trường hợp đặc biệt của vấn đề được mô tả trong câu hỏi. Chạy nó tại bàn điều khiển, không gõ bất kỳ đầu vào nào và chờ kết quả. Nó đã được thử nghiệm thành công theo Java 6 trên Windows và Linux.

import java.io.FileInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedByInterruptException;

public class Main {

    static final ByteBuffer buf = ByteBuffer.allocate(4096);

    public static void main(String[] args) {

        long timeout = 1000 * 5;

        try {
            InputStream in = extract(System.in);
            if (! (in instanceof FileInputStream))
                throw new RuntimeException(
                        "Could not extract a FileInputStream from STDIN.");

            try {
                int ret = maybeAvailable((FileInputStream)in, timeout);
                System.out.println(
                        Integer.toString(ret) + " bytes were read.");

            } finally {
                in.close();
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    /* unravels all layers of FilterInputStream wrappers to get to the
     * core InputStream
     */
    public static InputStream extract(InputStream in)
            throws NoSuchFieldException, IllegalAccessException {

        Field f = FilterInputStream.class.getDeclaredField("in");
        f.setAccessible(true);

        while( in instanceof FilterInputStream )
            in = (InputStream)f.get((FilterInputStream)in);

        return in;
    }

    /* Returns the number of bytes which could be read from the stream,
     * timing out after the specified number of milliseconds.
     * Returns 0 on timeout (because no bytes could be read)
     * and -1 for end of stream.
     */
    public static int maybeAvailable(final FileInputStream in, long timeout)
            throws IOException, InterruptedException {

        final int[] dataReady = {0};
        final IOException[] maybeException = {null};
        final Thread reader = new Thread() {
            public void run() {                
                try {
                    dataReady[0] = in.getChannel().read(buf);
                } catch (ClosedByInterruptException e) {
                    System.err.println("Reader interrupted.");
                } catch (IOException e) {
                    maybeException[0] = e;
                }
            }
        };

        Thread interruptor = new Thread() {
            public void run() {
                reader.interrupt();
            }
        };

        reader.start();
        for(;;) {

            reader.join(timeout);
            if (!reader.isAlive())
                break;

            interruptor.start();
            interruptor.join(1000);
            reader.join(1000);
            if (!reader.isAlive())
                break;

            System.err.println("We're hung");
            System.exit(1);
        }

        if ( maybeException[0] != null )
            throw maybeException[0];

        return dataReady[0];
    }
}

Thật thú vị, khi chạy chương trình bên trong NetBeans 6.5 chứ không phải ở bàn điều khiển, thời gian chờ hoàn toàn không hoạt động và lệnh gọi System.exit () thực sự cần thiết để tiêu diệt các luồng zombie. Điều gì xảy ra là chuỗi xử lý ngắt (!) Trên lệnh gọi reader.interrupt (). Một chương trình thử nghiệm khác (không được hiển thị ở đây) cũng cố gắng đóng kênh, nhưng điều đó cũng không hoạt động.


không hoạt động trên mac os, không phải với JDK 1.6 cũng như với JDK 1.7. Ngắt chỉ được nhận ra sau khi nhấn return trong quá trình đọc.
Mostowski Thu gọn

4

Như jt đã nói, NIO là giải pháp tốt nhất (và chính xác). Nếu bạn thực sự bị mắc kẹt với InputStream, bạn có thể

  1. Tạo ra một luồng công việc độc quyền của ai đó là đọc từ InputStream và đặt kết quả vào một bộ đệm có thể được đọc từ luồng gốc của bạn mà không bị chặn. Điều này sẽ hoạt động tốt nếu bạn chỉ có một phiên bản của luồng. Mặt khác, bạn có thể tiêu diệt luồng bằng các phương thức không dùng nữa trong lớp Thread, mặc dù điều này có thể gây rò rỉ tài nguyên.

  2. Dựa vào isAv Available để chỉ ra dữ liệu có thể được đọc mà không bị chặn. Tuy nhiên, trong một số trường hợp (chẳng hạn như với Ổ cắm), có thể mất khả năng chặn đọc đối với isAv Available để báo cáo nội dung nào đó ngoài 0.


5
Socket.setSoTimeout()là một giải pháp chính xác và đơn giản hơn nhiều. Hoặc HttpURLConnection.setReadTimeout().
Hầu tước Lorne

3
@EJP - đây chỉ là "chính xác như nhau" trong một số trường hợp nhất định; ví dụ: nếu luồng đầu vào là luồng kết nối / luồng kết nối HTTP.
Stephen C

1
@Stephen C NIO chỉ không chặn và có thể lựa chọn trong cùng trường hợp. Ví dụ, không có tệp I / O không chặn.
Hầu tước Lorne

2
@EJP nhưng có ống IO không chặn (System.in), I / O không chặn cho các tệp (trên đĩa cục bộ) là vô nghĩa
woky

1
@EJP Trên hầu hết (tất cả?) Unices System.in thực sự là một đường ống (nếu bạn không bảo shell thay thế nó bằng tệp) và như một đường ống, nó có thể không chặn.
woky

0

Lấy cảm hứng trong câu trả lời này, tôi đã đưa ra một giải pháp hướng đối tượng hơn một chút.

Điều này chỉ hợp lệ nếu bạn có ý định đọc các ký tự

Bạn có thể ghi đè BufferedReader và thực hiện một cái gì đó như thế này:

public class SafeBufferedReader extends BufferedReader{

    private long millisTimeout;

    ( . . . )

    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
        try {
            waitReady();
        } catch(IllegalThreadStateException e) {
            return 0;
        }
        return super.read(cbuf, off, len);
    }

    protected void waitReady() throws IllegalThreadStateException, IOException {
        if(ready()) return;
        long timeout = System.currentTimeMillis() + millisTimeout;
        while(System.currentTimeMillis() < timeout) {
            if(ready()) return;
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                break; // Should restore flag
            }
        }
        if(ready()) return; // Just in case.
        throw new IllegalThreadStateException("Read timed out");
    }
}

Đây là một ví dụ gần như hoàn chỉnh.

Tôi đang trả về 0 trên một số phương thức, bạn nên đổi nó thành -2 để đáp ứng nhu cầu của bạn, nhưng tôi nghĩ rằng 0 phù hợp hơn với hợp đồng BufferedReader. Không có gì sai xảy ra, nó chỉ đọc 0 ký tự. Phương pháp readLine là một kẻ giết người hiệu suất khủng khiếp. Bạn nên tạo một BufferedReader hoàn toàn mới nếu bạn thực sự muốn sử dụng readLin e. Ngay bây giờ, nó không phải là chủ đề an toàn. Nếu ai đó gọi một hoạt động trong khi readLines đang chờ một dòng, nó sẽ tạo ra kết quả không mong muốn

Tôi không thích trở về -2 nơi tôi đang ở. Tôi sẽ đưa ra một ngoại lệ vì một số người có thể đang kiểm tra nếu int <0 để xem xét EOS. Dù sao, những phương thức đó tuyên bố rằng "không thể chặn", bạn nên kiểm tra xem tuyên bố đó có thực sự đúng hay không và không ghi đè lên.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.nio.CharBuffer;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;

/**
 * 
 * readLine
 * 
 * @author Dario
 *
 */
public class SafeBufferedReader extends BufferedReader{

    private long millisTimeout;

    private long millisInterval = 100;

    private int lookAheadLine;

    public SafeBufferedReader(Reader in, int sz, long millisTimeout) {
        super(in, sz);
        this.millisTimeout = millisTimeout;
    }

    public SafeBufferedReader(Reader in, long millisTimeout) {
        super(in);
        this.millisTimeout = millisTimeout;
    }



    /**
     * This is probably going to kill readLine performance. You should study BufferedReader and completly override the method.
     * 
     * It should mark the position, then perform its normal operation in a nonblocking way, and if it reaches the timeout then reset position and throw IllegalThreadStateException
     * 
     */
    @Override
    public String readLine() throws IOException {
        try {
            waitReadyLine();
        } catch(IllegalThreadStateException e) {
            //return null; //Null usually means EOS here, so we can't.
            throw e;
        }
        return super.readLine();
    }

    @Override
    public int read() throws IOException {
        try {
            waitReady();
        } catch(IllegalThreadStateException e) {
            return -2; // I'd throw a runtime here, as some people may just be checking if int < 0 to consider EOS
        }
        return super.read();
    }

    @Override
    public int read(char[] cbuf) throws IOException {
        try {
            waitReady();
        } catch(IllegalThreadStateException e) {
            return -2;  // I'd throw a runtime here, as some people may just be checking if int < 0 to consider EOS
        }
        return super.read(cbuf);
    }

    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
        try {
            waitReady();
        } catch(IllegalThreadStateException e) {
            return 0;
        }
        return super.read(cbuf, off, len);
    }

    @Override
    public int read(CharBuffer target) throws IOException {
        try {
            waitReady();
        } catch(IllegalThreadStateException e) {
            return 0;
        }
        return super.read(target);
    }

    @Override
    public void mark(int readAheadLimit) throws IOException {
        super.mark(readAheadLimit);
    }

    @Override
    public Stream<String> lines() {
        return super.lines();
    }

    @Override
    public void reset() throws IOException {
        super.reset();
    }

    @Override
    public long skip(long n) throws IOException {
        return super.skip(n);
    }

    public long getMillisTimeout() {
        return millisTimeout;
    }

    public void setMillisTimeout(long millisTimeout) {
        this.millisTimeout = millisTimeout;
    }

    public void setTimeout(long timeout, TimeUnit unit) {
        this.millisTimeout = TimeUnit.MILLISECONDS.convert(timeout, unit);
    }

    public long getMillisInterval() {
        return millisInterval;
    }

    public void setMillisInterval(long millisInterval) {
        this.millisInterval = millisInterval;
    }

    public void setInterval(long time, TimeUnit unit) {
        this.millisInterval = TimeUnit.MILLISECONDS.convert(time, unit);
    }

    /**
     * This is actually forcing us to read the buffer twice in order to determine a line is actually ready.
     * 
     * @throws IllegalThreadStateException
     * @throws IOException
     */
    protected void waitReadyLine() throws IllegalThreadStateException, IOException {
        long timeout = System.currentTimeMillis() + millisTimeout;
        waitReady();

        super.mark(lookAheadLine);
        try {
            while(System.currentTimeMillis() < timeout) {
                while(ready()) {
                    int charInt = super.read();
                    if(charInt==-1) return; // EOS reached
                    char character = (char) charInt;
                    if(character == '\n' || character == '\r' ) return;
                }
                try {
                    Thread.sleep(millisInterval);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt(); // Restore flag
                    break;
                }
            }
        } finally {
            super.reset();
        }
        throw new IllegalThreadStateException("readLine timed out");

    }

    protected void waitReady() throws IllegalThreadStateException, IOException {
        if(ready()) return;
        long timeout = System.currentTimeMillis() + millisTimeout;
        while(System.currentTimeMillis() < timeout) {
            if(ready()) return;
            try {
                Thread.sleep(millisInterval);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); // Restore flag
                break;
            }
        }
        if(ready()) return; // Just in case.
        throw new IllegalThreadStateException("read timed out");
    }

}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.