Verwenden von inputStream.available ()
Es ist immer akzeptabel, dass System.in.available () 0 zurückgibt.
Ich habe das Gegenteil gefunden - es gibt immer den besten Wert für die Anzahl der verfügbaren Bytes zurück. Javadoc für 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.
Eine Schätzung ist aufgrund des Timings / der Alterung unvermeidbar. Die Zahl kann eine einmalige Unterschätzung sein, da ständig neue Daten eintreffen. Es "holt" jedoch immer beim nächsten Anruf auf - es sollte alle eingehenden Daten berücksichtigen, abgesehen davon, dass sie gerade zum Zeitpunkt des neuen Anrufs eintreffen. Die dauerhafte Rückgabe von 0, wenn Daten vorhanden sind, schlägt die obige Bedingung fehl.
Erste Einschränkung: Konkrete Unterklassen von InputStream sind für die Verfügbarkeit verantwortlich ()
InputStream
ist eine abstrakte Klasse. Es hat keine Datenquelle. Es ist bedeutungslos, Daten zur Verfügung zu haben. Daher heißt es in javadoc available()
auch:
The available method for class InputStream always returns 0.
This method should be overridden by subclasses.
Tatsächlich überschreiben die konkreten Eingabestreamklassen die verfügbaren () und liefern aussagekräftige Werte, keine konstanten Nullen.
Zweite Einschränkung: Stellen Sie sicher, dass Sie Wagenrücklauf verwenden, wenn Sie Eingaben in Windows eingeben.
Bei Verwendung System.in
erhält Ihr Programm nur Eingaben, wenn Ihre Befehlsshell sie übergibt. Wenn Sie Dateiumleitungen / Pipes verwenden (z. B. somefile> java myJavaApp oder somecommand | java myJavaApp), werden Eingabedaten normalerweise sofort übergeben. Wenn Sie die Eingabe jedoch manuell eingeben, kann sich die Datenübergabe verzögern. Beispiel: Bei der Windows-Shell cmd.exe werden die Daten in der Shell cmd.exe gepuffert. Daten werden erst nach Wagenrücklauf (control-m oder <enter>
) an das ausführende Java-Programm übergeben . Dies ist eine Einschränkung der Ausführungsumgebung. Natürlich gibt InputStream.available () 0 zurück, solange die Shell die Daten puffert - das ist korrektes Verhalten. Zu diesem Zeitpunkt sind keine Daten verfügbar. Sobald die Daten in der Shell verfügbar sind, gibt die Methode einen Wert> 0 zurück. Hinweis: Cygwin verwendet cmd.
Einfachste Lösung (keine Blockierung, daher keine Zeitüberschreitung erforderlich)
Verwenden Sie einfach dies:
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.
Oder gleichwertig,
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);
}
Reichhaltigere Lösung (füllt den Puffer innerhalb des Zeitlimits maximal aus)
Erklären Sie dies:
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;
}
Dann benutze dies:
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.
is.available() > 1024
dieser Vorschlag fehlschlägt. Es gibt sicherlich Streams, die Null zurückgeben. SSLSockets zum Beispiel bis vor kurzem. Darauf können Sie sich nicht verlassen.Angenommen, Ihr Stream wird nicht von einem Socket unterstützt (Sie können ihn also nicht verwenden
Socket.setSoTimeout()
). Ich denke, die Standardmethode zur Lösung dieser Art von Problem ist die Verwendung einer Zukunft.Angenommen, ich habe den folgenden Executor und Streams:
Ich habe einen Writer, der einige Daten schreibt und dann 5 Sekunden wartet, bevor er die letzten Daten schreibt und den Stream schließt:
Die normale Art, dies zu lesen, ist wie folgt. Der Lesevorgang wird auf unbestimmte Zeit für Daten blockiert und ist daher in 5 Sekunden abgeschlossen:
welche Ausgänge:
Wenn es ein grundlegenderes Problem gäbe, wie wenn der Autor nicht antwortet, würde der Leser für immer blockieren. Wenn ich den Lesevorgang in einer Zukunft abschließe, kann ich das Zeitlimit wie folgt steuern:
welche Ausgänge:
Ich kann die TimeoutException abfangen und jede gewünschte Bereinigung durchführen.
quelle
executer.shutdownNow
wird den Thread nicht töten. Es wird versuchen, es ohne Wirkung zu unterbrechen. Es ist keine Bereinigung möglich und dies ist ein ernstes Problem.Wenn Ihr InputStream von einem Socket unterstützt wird, können Sie mit setSoTimeout ein Socket-Timeout (in Millisekunden) festlegen . Wenn der Aufruf von read () nicht innerhalb des angegebenen Zeitlimits entsperrt wird, wird eine SocketTimeoutException ausgelöst.
Stellen Sie einfach sicher, dass Sie setSoTimeout am Socket aufrufen, bevor Sie den Aufruf read () ausführen.
quelle
Ich würde die Problemstellung in Frage stellen, anstatt sie nur blind zu akzeptieren. Sie benötigen nur Zeitüberschreitungen von der Konsole oder über das Netzwerk. Wenn Sie letztere haben
Socket.setSoTimeout()
undHttpURLConnection.setReadTimeout()
beide genau das tun, was erforderlich ist, solange Sie sie beim Erstellen / Erwerb korrekt eingerichtet haben. Lassen Sie es später in der Anwendung an einem beliebigen Punkt, wenn Sie nur den InputStream haben. Das schlechte Design führt zu einer sehr umständlichen Implementierung.quelle
Ich habe die Klassen aus dem Java NIO-Paket nicht verwendet, aber es scheint, dass sie hier hilfreich sein könnten. Insbesondere java.nio.channels.Channels und java.nio.channels.InterruptibleChannel .
quelle
Hier ist eine Möglichkeit, einen NIO FileChannel von System.in abzurufen und die Verfügbarkeit von Daten mithilfe eines Timeouts zu überprüfen. Dies ist ein Sonderfall des in der Frage beschriebenen Problems. Führen Sie es an der Konsole aus, geben Sie keine Eingabe ein und warten Sie auf die Ergebnisse. Es wurde erfolgreich unter Java 6 unter Windows und Linux getestet.
Interessanterweise funktioniert das Timeout beim Ausführen des Programms in NetBeans 6.5 und nicht an der Konsole überhaupt nicht, und der Aufruf von System.exit () ist tatsächlich erforderlich, um die Zombiethreads zu beenden. Was passiert ist, dass der Interruptor-Thread (!) Beim Aufruf von reader.interrupt () blockiert. Ein anderes Testprogramm (hier nicht gezeigt) versucht zusätzlich, den Kanal zu schließen, aber das funktioniert auch nicht.
quelle
Wie gesagt, NIO ist die beste (und richtige) Lösung. Wenn Sie jedoch wirklich mit einem InputStream nicht weiterkommen, können Sie dies auch tun
Erstellen Sie einen Thread, dessen exklusive Aufgabe darin besteht, aus dem InputStream zu lesen und das Ergebnis in einen Puffer zu legen, der ohne Blockierung aus Ihrem ursprünglichen Thread gelesen werden kann. Dies sollte gut funktionieren, wenn Sie immer nur eine Instanz des Streams haben. Andernfalls können Sie den Thread möglicherweise mit den veralteten Methoden in der Thread-Klasse beenden. Dies kann jedoch zu Ressourcenlecks führen.
Verlassen Sie sich auf isAvailable, um Daten anzuzeigen, die ohne Blockierung gelesen werden können. In einigen Fällen (z. B. bei Sockets) kann es jedoch zu einem potenziell blockierenden Lesevorgang kommen, damit isAvailable etwas anderes als 0 meldet.
quelle
Socket.setSoTimeout()
ist eine ebenso korrekte und viel einfachere Lösung. OderHttpURLConnection.setReadTimeout()
.Inspiriert von dieser Antwort habe ich eine etwas objektorientiertere Lösung gefunden.
Dies gilt nur, wenn Sie Zeichen lesen möchten
Sie können BufferedReader überschreiben und Folgendes implementieren:
Hier ist ein fast vollständiges Beispiel.
Ich gebe bei einigen Methoden 0 zurück. Sie sollten es auf -2 ändern, um Ihre Anforderungen zu erfüllen, aber ich denke, dass 0 für BufferedReader-Verträge besser geeignet ist. Es ist nichts Falsches passiert, es wurden nur 0 Zeichen gelesen. Die readLine-Methode ist ein schrecklicher Leistungskiller. Sie sollten einen völlig neuen BufferedReader erstellen, wenn Sie readLin tatsächlich verwenden möchten e . Im Moment ist es nicht threadsicher. Wenn jemand eine Operation aufruft, während readLines auf eine Zeile wartet, führt dies zu unerwarteten Ergebnissen
Ich mag es nicht, -2 zurückzugeben, wo ich bin. Ich würde eine Ausnahme auslösen, da einige Leute möglicherweise nur prüfen, ob int <0 ist, um EOS zu berücksichtigen. Wie auch immer, diese Methoden behaupten, dass "nicht blockieren kann". Sie sollten überprüfen, ob diese Aussage tatsächlich wahr ist, und sie einfach nicht überschreiben.
quelle