2010-01-23 34 views
7

Tôi có một số BufferedReader (được tạo bởi new BufferedReader(new InputStreamReader(process.getInputStream()))). Tôi khá mới với khái niệm về một BufferedReader nhưng như tôi thấy nó, nó có ba trạng thái:Làm cách nào để xác định trạng thái chính xác của BufferedReader?

  1. Một dòng đang chờ đọc; gọi bufferedReader.readLine sẽ trả lại chuỗi này ngay lập tức.
  2. Luồng đang mở, nhưng không có dòng nào chờ đọc; gọi bufferedReader.readLine sẽ treo chuỗi cho đến khi một dòng có sẵn.
  3. Luồng đã bị đóng; gọi số bufferedReader.readLine sẽ trả về giá trị rỗng.

Bây giờ tôi muốn xác định trạng thái của BufferedReader, để tôi có thể xác định liệu tôi có thể đọc an toàn từ nó mà không cần treo ứng dụng của mình hay không. Quá trình cơ bản (xem ở trên) nổi tiếng là không đáng tin cậy và vì vậy có thể đã treo; trong trường hợp này, tôi không muốn ứng dụng máy chủ của mình bị treo. Vì vậy, tôi đang thực hiện một loại thời gian chờ. Tôi đã cố gắng để làm điều này đầu tiên với luồng nhưng nó đã phức tạp khủng khiếp.

Gọi BufferedReader.ready() sẽ không phân biệt giữa các trường hợp (2) và (3) ở trên. Nói cách khác, nếu ready() trả về false, có thể là luồng chỉ đóng (nói cách khác, quy trình cơ bản của tôi đã đóng một cách duyên dáng) hoặc có thể là quy trình cơ bản đã treo.

Vì vậy, câu hỏi của tôi là: làm cách nào để xác định ba trạng thái nào trong số ba trạng thái này của tôi là BufferedReader nằm trong thực tế mà không gọi số readLine? Rất tiếc, tôi không thể chỉ gọi số readLine để kiểm tra điều này vì ứng dụng của tôi sẽ bị treo.

Tôi đang sử dụng phiên bản JDK 1.5.

+0

Nhân tiện, tôi giả định rằng thời điểm quá trình cơ bản của tôi không thể treo ở giữa một dòng .... mặc dù đó có thể là một giả định sai lầm, nó đã không xảy ra cho đến nay trong khi thử nghiệm và tôi đã có đủ các vấn đề khác !! – Kidburla

+0

N.B. Tôi hiện đang tìm kiếm các tùy chọn khác hơn là luồng. Vấn đề là ứng dụng của tôi không hoạt động vào lúc này (vì một số lý do bao gồm readLine) và tôi thấy các chủ đề rất khó gỡ lỗi, đặc biệt nếu chúng đang hết thời gian chờ. – Kidburla

Trả lời

2

Cuối cùng tôi đã tìm được giải pháp cho điều này. Hầu hết các câu trả lời ở đây dựa vào các chủ đề, nhưng như tôi đã chỉ ra trước đó, tôi đang tìm một giải pháp không yêu cầu luồng. Tuy nhiên, cơ sở của tôi là quá trình. Những gì tôi thấy là các quá trình dường như thoát ra nếu cả đầu ra (được gọi là "đầu vào") và các luồng lỗi đều trống và đóng. Điều này có ý nghĩa nếu bạn nghĩ về nó.

Vì vậy, tôi chỉ thăm dò ý kiến ​​các đầu ra và các luồng lỗi và cũng đã cố gắng xác định xem quy trình đã thoát hay chưa. Dưới đây là một bản sao thô của giải pháp của tôi.

public String readLineWithTimeout(Process process, long timeout) throws IOException, TimeoutException { 
    BufferedReader output = new BufferedReader(new InputStreamReader(process.getInputStream())); 
    BufferedReader error = new BufferedReader(new InputStreamReader(process.getErrorStream())); 
    boolean finished = false; 
    long startTime = 0; 
    while (!finished) { 
    if (output.ready()) { 
     return output.readLine(); 
    } else if (error.ready()) { 
     error.readLine(); 
    } else { 
     try { 
     process.exitValue(); 
     return null; 
     } catch (IllegalThreadStateException ex) { 
     //Expected behaviour 
     } 
    } 
    if (startTime == 0) { 
     startTime = System.currentTimeMills(); 
    } else if (System.currentTimeMillis() > startTime + timeout) { 
     throw new TimeoutException(); 
    } 
    } 
} 
+1

Tôi nghi ngờ điều này vẫn có thể bị chặn khi Reader # ready() cho biết tính khả dụng của một ký tự đơn (Hoặc nhiều hơn). Tôi cũng nghi ngờ rằng có thể có trường hợp khi tất cả các độc giả báo cáo sai trong ready() nhưng đường ống của quá trình có dữ liệu để đọc với một cuộc gọi hệ thống có khả năng chặn(). –

+0

Tôi biết điều này được trả lời. Nhưng kể từ khi tôi bị mắc kẹt với chính xác tình hình này hai lần. Một gợi ý: bạn cần phải chờ đợi trước khi bất kỳ cố gắng để đọc từ một quá trình được excuted. bạn có thể chỉ bắt đầu đọc khi quá trình này được thực hiện đầy đủ, có nghĩa là ready() hoặc readLine() có thể chặn không chính xác ... – rubmz

0

Bạn đã xác nhận bằng cách thử nghiệm xác nhận của bạn rằng ready() sẽ trả về false ngay cả khi luồng cơ bản ở cuối tệp? Bởi vì tôi không mong đợi khẳng định đó là chính xác (mặc dù tôi chưa thực hiện thí nghiệm).

+0

Thật không may là anh ấy đúng. Đó là một trong những "gotchas" lớn nhất trong Java IO, và nó làm tôi buồn: ( – ZoFreX

0

Bạn có thể sử dụng InputStream.available() để xem liệu có đầu ra mới từ quy trình hay không. Điều này sẽ làm việc theo cách bạn muốn nếu quá trình này chỉ xuất ra các dòng đầy đủ, nhưng nó không thực sự đáng tin cậy.

Một cách tiếp cận đáng tin cậy hơn cho vấn đề sẽ là có một chuỗi riêng biệt dành riêng cho việc đọc từ quy trình và đẩy mọi dòng nó đọc vào hàng đợi hoặc người tiêu dùng.

+0

Does InputStream).có sẵn() phân biệt giữa các trường hợp (2) và (3) trong bài đăng gốc của tôi tốt hơn so với sẵn sàng() không? Nếu vậy, tại sao bạn nói nó không đáng tin cậy? – Kidburla

+0

Reader.ready() cuối cùng sử dụng InputStream.available() để xác định xem có bất kỳ dữ liệu nào có sẵn hay không. Vì vậy, nó không phải là nhiều hơn và cũng không đáng tin cậy. Vấn đề là nó không được đảm bảo rằng InputStream.available() sẽ báo cáo bất kỳ dữ liệu có sẵn ngay cả khi có một số. Điều này là do luồng có thể không thể tìm ra nếu có bất kỳ thứ gì khả dụng mà không bị chặn. Và ngay cả khi nó báo cáo một cái gì đó, bạn không thể chắc chắn rằng nó sẽ kết thúc với một linefeed. Nếu nó không kết thúc với một linefeed, gọi readLine trên ready() == true vẫn sẽ chặn. Tôi khuyên bạn nên sử dụng một chuỗi chuyên dụng. – x4u

+0

Cảm ơn, âm thanh với tôi như có sẵn() không phân biệt giữa các trường hợp (2) và (3) tốt hơn so với ready(), vì vậy điều này có thể không giải quyết được vấn đề của tôi. – Kidburla

1

Đây là vấn đề khá cơ bản với API I/O chặn của java.

Tôi nghi ngờ bạn sẽ muốn chọn một trong số:

(1) Truy cập lại ý tưởng sử dụng luồng. Điều này không phải là phức tạp, thực hiện đúng cách, và nó sẽ cho phép mã của bạn thoát khỏi một tôi chặn/O đọc khá duyên dáng, ví dụ:

final BufferedReader reader = ... 
ExecutorService executor = // create an executor here, using the Executors factory class. 
Callable<String> task = new Callable<String> { 
    public String call() throws IOException { 
     return reader.readLine(); 
    } 
}; 
Future<String> futureResult = executor.submit(task); 
String line = futureResult.get(timeout); // throws a TimeoutException if the read doesn't return in time 

(2) Sử dụng java.nio thay vì java.io. Đây là một API phức tạp hơn, nhưng nó có ngữ nghĩa không chặn.

2

Có một trạng thái trong đó một số dữ liệu có thể nằm trong bộ đệm, nhưng không nhất thiết phải điền vào một dòng. Trong trường hợp này, ready() sẽ trả lại true, nhưng gọi số readLine() sẽ chặn.

Bạn có thể dễ dàng xây dựng các phương pháp ready()readLine() của riêng mình. ready() của bạn thực sự sẽ cố gắng xây dựng một dòng, và chỉ khi nó đã làm như vậy thành công nó sẽ trở lại true. Sau đó, readLine() của bạn có thể trả lại dòng được định dạng đầy đủ.

+0

Tôi tin rằng đây là phương pháp tốt nhất trừ khi cần thêm hiệu suất đệm. – ZoFreX

+0

Điều này khác với giải pháp của bạn như thế nào mặc dù ZoFrex? Có vẻ như tùy chỉnh sẵn sàng của tôi() sẽ phải gọi một số byte ở mức sẵn sàng() bên dưới, không thể đảm bảo liệu read() có chặn hay không. Vẫn mở ứng dụng của tôi để có thể treo nếu quá trình cơ bản bị treo. – Kidburla

+0

Nhìn qua mã nguồn Java, tôi thấy rằng BufferedReader # ready() là cần thiết bảo thủ vì nó kiểm tra bộ đệm riêng và trình đọc cơ bản như InputStreamReader. http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/io/BufferedReader.java Tôi đoán InputStreamReader thứ hai cũng sẽ báo cáo trạng thái ready() của nó một cách thận trọng . –

0

Nói chung, bạn phải triển khai tính năng này với nhiều chuỗi. Có những trường hợp đặc biệt, như đọc từ ổ cắm, nơi luồng cơ bản có sẵn một cơ sở thời gian chờ.

Tuy nhiên, không quá phức tạp để thực hiện việc này với nhiều chuỗi. Đây là một mô hình tôi sử dụng:

private static final ExecutorService worker = 
    Executors.newSingleThreadExecutor(); 

private static class Timeout implements Callable<Void> { 
    private final Closeable target; 
    private Timeout(Closeable target) { 
    this.target = target; 
    } 
    public Void call() throws Exception { 
    target.close(); 
    return null; 
    } 
} 

... 

InputStream stream = process.getInputStream(); 
Future<?> task = worker.schedule(new Timeout(stream), 5, TimeUnit.SECONDS); 
/* Use the stream as you wish. If it hangs for more than 5 seconds, 
    the underlying stream is closed, raising an IOException here. */ 
... 
/* If you get here without timing out, cancel the asynchronous timeout 
    and close the stream explicitly. */ 
if(task.cancel(false)) 
    stream.close(); 
0

Bạn có thể làm wrapper của riêng bạn xung quanh InputStream hay InputStreamReader hoạt động trên một mức độ byte-by-byte, mà sẵn sàng() trả về giá trị chính xác.

Các tùy chọn khác của bạn là luồng có thể được thực hiện đơn giản (xem xét một số cấu trúc dữ liệu đồng thời mà Java cung cấp) và NIO, rất phức tạp và có thể quá mức cần thiết.

+0

Điều này nghe giống như lựa chọn tốt nhất của tôi (tương tự với câu trả lời của lavinio), nhưng khi bạn nói "ready() trả về giá trị chính xác", làm thế nào sẵn sàng() phân biệt giữa các trường hợp của tôi (2) và (3)? – Kidburla

+0

Ngẫu nhiên, InputStream không hỗ trợ phương thức ready() và InputStreamReader hỗ trợ nó nhưng javadoc vẫn nói "Lưu ý rằng trả về false không đảm bảo rằng lần đọc kế tiếp sẽ chặn." – Kidburla

+0

Nếu nó trả về true, tuy nhiên nó đảm bảo rằng lần đọc tiếp theo sẽ không chặn. Tôi nghĩ. Tôi không biết cách phân biệt giữa các trường hợp 2 và 3 và chỉ có thể đề xuất thử nghiệm nó! – ZoFreX

0

Nếu bạn chỉ muốn thời gian chờ thì các phương pháp khác ở đây có thể tốt hơn. Nếu bạn muốn có một người đọc non-blocking đệm, dưới đây là cách tôi sẽ làm điều đó, với chủ đề: (xin lưu ý tôi đã không kiểm tra này và ít nhất nó cần một số ngoại lệ xử lý bổ sung)

public class MyReader implements Runnable { 
    private final BufferedReader reader; 
    private ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<String>(); 
    private boolean closed = false; 

    public MyReader(BufferedReader reader) { 
     this.reader = reader; 
    } 

    public void run() { 
     String line; 
     while((line = reader.readLine()) != null) { 
      queue.add(line); 
     } 
     closed = true; 
    } 

    // Returns true iff there is at least one line on the queue 
    public boolean ready() { 
     return(queue.peek() != null); 
    } 

    // Returns true if the underlying connection has closed 
    // Note that there may still be data on the queue! 
    public boolean isClosed() { 
     return closed; 
    } 

    // Get next line 
    // Returns null if there is none 
    // Never blocks 
    public String readLine() { 
     return(queue.poll()); 
    } 
} 

Sau đây là cách để sử dụng nó:

BufferedReader b; // Initialise however you normally do 
MyReader reader = new MyReader(b); 
new Thread(reader).start(); 

// True if there is data to be read regardless of connection state 
reader.ready(); 

// True if the connection is closed 
reader.closed(); 

// Gets the next line, never blocks 
// Returns null if there is no data 
// This doesn't necessarily mean the connection is closed, it might be waiting! 
String line = reader.readLine(); // Gets the next line 

có bốn trạng thái có thể:

  1. kết nối đang mở, không có dữ liệu có sẵn
  2. kết nối đang mở, dữ liệu là sẵn
  3. kết nối được đóng lại, dữ liệu có sẵn
  4. kết nối được đóng lại, không có dữ liệu có sẵn

Bạn có thể phân biệt giữa chúng với isClosed() và sẵn sàng) phương pháp (.

Các vấn đề liên quan