Menggunakan inputStream.available ()
Itu selalu dapat diterima untuk System.in.available () untuk mengembalikan 0.
Saya telah menemukan yang sebaliknya - selalu mengembalikan nilai terbaik untuk jumlah byte yang tersedia. Javadoc untuk 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.
Estimasi tidak dapat dihindari karena waktu / staleness. Angka tersebut bisa menjadi perkiraan sekali saja karena data baru terus berdatangan. Namun itu selalu "mengejar" pada panggilan berikutnya - itu harus memperhitungkan semua data yang tiba, bar yang tiba tepat pada saat panggilan baru. Mengembalikan 0 secara permanen ketika ada data yang gagal dengan kondisi di atas.
Peringatan Pertama: Subkelas beton InputStream bertanggung jawab atas ketersediaan ()
InputStream
adalah kelas abstrak. Tidak memiliki sumber data. Tidak ada artinya untuk memiliki data yang tersedia. Karenanya, javadoc untuk available()
juga menyatakan:
The available method for class InputStream always returns 0.
This method should be overridden by subclasses.
Dan memang, kelas stream input konkret melakukan override available (), memberikan nilai yang bermakna, bukan 0s konstan.
Peringatan Kedua: Pastikan Anda menggunakan carriage-return saat mengetik input di Windows.
Jika menggunakan System.in
, program Anda hanya menerima input ketika shell perintah Anda menyerahkannya. Jika Anda menggunakan pengalihan file / pipa (mis. Somefile> java myJavaApp atau somecommand | java myJavaApp), maka input data biasanya diserahkan segera. Namun, jika Anda mengetik input secara manual, maka penyerahan data dapat ditunda. Misalnya Dengan shell windows cmd.exe, data di buffer dalam shell cmd.exe. Data hanya diteruskan ke program java yang menjalankan mengikuti carriage-return (control-m atau <enter>
). Itu batasan lingkungan eksekusi. Tentu saja, InputStream.available () akan mengembalikan 0 selama shell buffer data - itu perilaku yang benar; tidak ada data yang tersedia pada saat itu. Segera setelah data tersedia dari shell, metode mengembalikan nilai> 0. NB: Cygwin menggunakan cmd.
Solusi paling sederhana (tidak ada pemblokiran, jadi tidak perlu waktu habis)
Gunakan ini saja:
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.
ATAU setara,
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);
}
Richer Solution (secara maksimal mengisi buffer dalam periode waktu habis)
Nyatakan ini:
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;
}
Kemudian gunakan ini:
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
saran ini akan gagal. Tentu saja ada aliran yang mengembalikan nol. SSLSockets misalnya hingga saat ini. Anda tidak dapat mengandalkan ini.Dengan asumsi aliran Anda tidak didukung oleh soket (sehingga Anda tidak dapat menggunakan
Socket.setSoTimeout()
), saya pikir cara standar untuk memecahkan masalah jenis ini adalah dengan menggunakan Masa Depan.Misalkan saya memiliki pelaksana dan aliran berikut:
Saya memiliki penulis yang menulis beberapa data kemudian menunggu selama 5 detik sebelum menulis bagian terakhir dari data dan menutup aliran:
Cara membaca yang normal adalah sebagai berikut. Bacaan akan memblokir tanpa batas untuk data dan ini selesai dalam 5s:
yang keluaran:
Jika ada masalah yang lebih mendasar, seperti penulis tidak merespons, pembaca akan memblokir selamanya. Jika saya membungkus pembacaan di masa depan, saya kemudian dapat mengontrol batas waktu sebagai berikut:
yang keluaran:
Saya dapat menangkap TimeoutException dan melakukan pembersihan apa pun yang saya inginkan.
sumber
executer.shutdownNow
tidak akan mematikan utas. Itu akan mencoba untuk menghentikannya, tanpa efek. Tidak ada pembersihan yang mungkin dan ini adalah masalah serius.Jika InputStream Anda didukung oleh Socket, Anda dapat mengatur timeout Socket (dalam milidetik) menggunakan setSoTimeout . Jika panggilan read () tidak membuka blokir dalam batas waktu yang ditentukan, itu akan melempar SocketTimeoutException.
Pastikan Anda memanggil setSoTimeout di Socket sebelum melakukan panggilan read ().
sumber
Saya akan mempertanyakan pernyataan masalah daripada hanya menerimanya secara membabi buta. Anda hanya perlu waktu tunggu dari konsol atau melalui jaringan. Jika yang terakhir Anda miliki
Socket.setSoTimeout()
danHttpURLConnection.setReadTimeout()
yang keduanya melakukan persis apa yang diperlukan, selama Anda mengaturnya dengan benar ketika Anda membangun / memperolehnya. Membiarkannya ke titik arbitrer kemudian dalam aplikasi ketika semua yang Anda miliki adalah InputStream adalah desain yang buruk yang mengarah ke implementasi yang sangat canggung.sumber
Saya belum menggunakan kelas-kelas dari paket Java NIO, tetapi tampaknya mereka bisa membantu di sini. Khususnya, java.nio.channels.Channels dan java.nio.channels.InterruptibleChannel .
sumber
Berikut adalah cara untuk mendapatkan NIO FileChannel dari System.in dan memeriksa ketersediaan data menggunakan batas waktu, yang merupakan kasus khusus dari masalah yang dijelaskan dalam pertanyaan. Jalankan di konsol, jangan ketik input apa pun, dan tunggu hasilnya. Itu diuji dengan sukses di bawah Java 6 pada Windows dan Linux.
Menariknya, ketika menjalankan program di dalam NetBeans 6.5 daripada di konsol, batas waktu tidak berfungsi sama sekali, dan panggilan ke System.exit () sebenarnya diperlukan untuk membunuh utas zombie. Apa yang terjadi adalah bahwa thread interruptor memblokir (!) Pada panggilan ke reader.interrupt (). Program pengujian lain (tidak ditampilkan di sini) juga mencoba menutup saluran, tetapi itu juga tidak berhasil.
sumber
Seperti dikatakan jt, NIO adalah solusi terbaik (dan benar). Jika Anda benar-benar terjebak dengan InputStream, Anda juga bisa
Tumbuhkan utas yang pekerjaan eksklusifnya adalah membaca dari InputStream dan memasukkan hasilnya ke dalam buffer yang dapat dibaca dari utas asli Anda tanpa memblokir. Ini akan bekerja dengan baik jika Anda hanya memiliki satu contoh aliran. Jika tidak, Anda mungkin dapat mematikan utas menggunakan metode yang tidak digunakan di kelas Utas, meskipun ini dapat menyebabkan kebocoran sumber daya.
Andalkan isAvailable untuk menunjukkan data yang dapat dibaca tanpa memblokir. Namun dalam beberapa kasus (seperti dengan Soket) dapat membaca pemblokiran yang potensial untuk tersedia untuk melaporkan sesuatu selain 0.
sumber
Socket.setSoTimeout()
adalah solusi yang sama benar dan jauh lebih sederhana. AtauHttpURLConnection.setReadTimeout()
.Terinspirasi dalam jawaban ini saya datang dengan solusi yang lebih berorientasi objek.
Ini hanya valid jika Anda bermaksud membaca karakter
Anda dapat mengganti BufferedReader dan mengimplementasikan sesuatu seperti ini:
Ini contoh yang hampir lengkap.
Saya mengembalikan 0 pada beberapa metode, Anda harus mengubahnya ke -2 untuk memenuhi kebutuhan Anda, tapi saya pikir 0 lebih cocok dengan kontrak BufferedReader. Tidak ada yang salah terjadi, ia hanya membaca 0 karakter. Metode readLine adalah pembunuh kinerja yang mengerikan. Anda harus membuat BufferedReader yang sama sekali baru jika Anda benar-benar ingin menggunakan readLin e. Saat ini, tidak aman untuk thread. Jika seseorang menjalankan operasi ketika readLines menunggu baris, itu akan menghasilkan hasil yang tidak terduga
Saya tidak suka kembali -2 di tempat saya. Saya akan melemparkan pengecualian karena beberapa orang mungkin hanya memeriksa jika int <0 untuk mempertimbangkan EOS. Bagaimanapun, metode-metode tersebut mengklaim bahwa "tidak dapat memblokir", Anda harus memeriksa apakah pernyataan itu benar dan hanya jangan menimpanya.
sumber