Usando inputStream.available ()
É sempre aceitável que System.in.available () retorne 0.
Eu descobri o contrário - ele sempre retorna o melhor valor para o número de bytes disponíveis. Javadoc para 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.
Uma estimativa é inevitável devido ao tempo / estabilidade. A figura pode ser uma subestimação pontual porque novos dados estão chegando constantemente. No entanto, ele sempre "alcança" a próxima chamada - deve ser responsável por todos os dados recebidos, exceto pelos que chegam exatamente no momento da nova chamada. Retornar permanentemente 0 quando houver dados falha na condição acima.
Primeira advertência: as subclasses de concreto do InputStream são responsáveis pela disponibilidade ()
InputStream
é uma classe abstrata. Não possui fonte de dados. Não faz sentido ter dados disponíveis. Portanto, o javadoc para available()
também declara:
The available method for class InputStream always returns 0.
This method should be overridden by subclasses.
E, de fato, as classes concretas do fluxo de entrada substituem available (), fornecendo valores significativos, não 0s constantes.
Segunda Advertência: Certifique-se de usar retorno de carro ao digitar entrada no Windows.
Se estiver usando System.in
, seu programa só recebe entrada quando seu shell de comando o entrega. Se você estiver usando redirecionamentos de arquivo / pipes (por exemplo, somefile> java myJavaApp ou algum comando | java myJavaApp), os dados de entrada geralmente são entregues imediatamente. No entanto, se você digitar manualmente a entrada, a transferência de dados poderá ser atrasada. Por exemplo, com o shell do cmd.exe do windows, os dados são armazenados em buffer no shell do cmd.exe. Os dados são passados apenas para o programa java em execução após retorno de carro (control-m ou <enter>
). Essa é uma limitação do ambiente de execução. Obviamente, InputStream.available () retornará 0 enquanto o shell armazenar em buffer os dados - esse é o comportamento correto; não há dados disponíveis nesse ponto. Assim que os dados estiverem disponíveis no shell, o método retornará um valor> 0. NB: Cygwin usa cmd.
Solução mais simples (sem bloqueio, portanto, não é necessário tempo limite)
Apenas use isto:
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.
Ou equivalente,
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);
}
Solução mais rica (preenche o buffer no máximo dentro do período de tempo limite)
Declare isso:
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;
}
Então use isto:
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
essa sugestão falhar. Certamente existem fluxos que retornam zero. SSLSockets, por exemplo, até recentemente. Você não pode confiar nisso.Supondo que seu fluxo não seja suportado por um soquete (então você não pode usá-lo
Socket.setSoTimeout()
), acho que a maneira padrão de resolver esse tipo de problema é usar um futuro.Suponha que eu tenha o seguinte executor e fluxos:
Eu tenho um escritor que grava alguns dados e aguarda 5 segundos antes de gravar o último dado e fechar o fluxo:
A maneira normal de ler isso é a seguinte. A leitura bloqueará indefinidamente os dados e, portanto, isso será concluído em 5s:
quais saídas:
Se houvesse um problema mais fundamental, como o escritor não responder, o leitor iria bloquear para sempre. Se eu encerrar a leitura em um futuro, posso controlar o tempo limite da seguinte maneira:
quais saídas:
Eu posso pegar a TimeoutException e fazer a limpeza que eu quiser.
fonte
executer.shutdownNow
não vai matar o fio. Ele tentará interrompê-lo, sem efeito. Não há limpeza possível e esse é um problema sério.Se o seu InputStream for apoiado por um Socket, você poderá definir um tempo limite de Socket (em milissegundos) usando setSoTimeout . Se a chamada read () não desbloquear dentro do tempo limite especificado, lançará uma SocketTimeoutException.
Apenas certifique-se de chamar setSoTimeout no soquete antes de fazer a chamada read ().
fonte
Eu questionaria a declaração do problema em vez de apenas aceitá-la cegamente. Você só precisa de tempos limite no console ou na rede. Se o último que você possui
Socket.setSoTimeout()
e osHttpURLConnection.setReadTimeout()
dois fazem exatamente o que é necessário, desde que você os configure corretamente ao construí-los / adquiri-los. Deixando-o para um ponto arbitrário mais tarde no aplicativo, quando tudo o que você tem é o InputStream é um design ruim, levando a uma implementação muito complicada.fonte
Eu não usei as classes do pacote Java NIO, mas parece que elas podem ser de alguma ajuda aqui. Especificamente, java.nio.channels.Channels e java.nio.channels.InterruptibleChannel .
fonte
Aqui está uma maneira de obter um NIO FileChannel do System.in e verificar a disponibilidade de dados usando um tempo limite, que é um caso especial do problema descrito na pergunta. Execute-o no console, não digite nenhuma entrada e aguarde os resultados. Foi testado com sucesso no Java 6 no Windows e Linux.
Curiosamente, ao executar o programa no NetBeans 6.5 e não no console, o tempo limite não funciona e a chamada para System.exit () é realmente necessária para eliminar os threads zumbis. O que acontece é que o encadeamento do interruptor bloqueia (!) A chamada para reader.interrupt (). Outro programa de teste (não mostrado aqui) também tenta fechar o canal, mas isso também não funciona.
fonte
Como disse jt, o NIO é a melhor (e correta) solução. Se você realmente está preso a um InputStream, você pode
Gera um thread cujo trabalho exclusivo é ler o InputStream e colocar o resultado em um buffer que pode ser lido no seu thread original sem bloquear. Isso deve funcionar bem se você tiver apenas uma instância do fluxo. Caso contrário, você poderá eliminar o encadeamento usando os métodos descontinuados na classe Thread, embora isso possa causar vazamentos de recursos.
Confie em isAvailable para indicar dados que podem ser lidos sem bloqueio. No entanto, em alguns casos (como em Sockets), pode levar uma leitura potencialmente bloqueadora para isAvailable para relatar algo diferente de 0.
fonte
Socket.setSoTimeout()
é uma solução igualmente correta e muito mais simples. OrHttpURLConnection.setReadTimeout()
.Inspirado nesta resposta , criei uma solução um pouco mais orientada a objetos.
Isso é válido apenas se você pretende ler caracteres
Você pode substituir o BufferedReader e implementar algo como isto:
Aqui está um exemplo quase completo.
Estou retornando 0 em alguns métodos, você deve alterá-lo para -2 para atender às suas necessidades, mas acho que 0 é mais adequado ao contrato BufferedReader. Nada de errado aconteceu, basta ler 0 caracteres. O método readLine é um assassino de desempenho horrível. Você deve criar um BufferedReader totalmente novo se realmente quiser usar o readLin e. No momento, não é seguro para threads. Se alguém chamar uma operação enquanto o readLines estiver aguardando uma linha, ele produzirá resultados inesperados
Não gosto de retornar -2 onde estou. Eu lançaria uma exceção, porque algumas pessoas podem apenas verificar se int <0 deve considerar o EOS. De qualquer forma, esses métodos afirmam que "não pode bloquear", você deve verificar se essa afirmação é realmente verdadeira e simplesmente não a substitui.
fonte