Можно ли читать из InputStream с таймаутом?

Вы можете использовать класс NetworkChange с событием NetworkAvailabilityChanged:

NetworkChange.NetworkAvailabilityChanged += myNetworkAvailabilityChangeHandler;

Поскольку это системное событие, убедитесь, что вы удалили событие, когда закончите, см. этот пост здесь: Вы должны быть осторожны с использованием обработчика событий для NetworkChange

140
задан Gray 18 October 2012 в 16:27
поделиться

3 ответа

Я не использовал классы из пакета Java NIO, но кажется , что они могут быть здесь полезны. В частности, java.nio.channels.Channels и java.nio.channels.InterruptibleChannel .

7
ответ дан 23 November 2019 в 23:05
поделиться

Вот способ получить NIO FileChannel из System.in и проверить доступность данных, используя тайм-аут, который является частным случаем проблемы, описанной в вопросе. Запустите его на консоли, не вводите никаких данных и ждите результатов. Он был успешно протестирован под Java 6 в Windows и 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];
    }
}

Интересно, что при запуске программы внутри NetBeans 6.5, а не на консоли, тайм-аут не работает вообще, и вызов System.exit () на самом деле необходимо убить зомби темы. Что происходит, так это то, что поток прерывания блокирует (!) При вызове reader.interrupt (). Другая тестовая программа (здесь не показана) дополнительно пытается закрыть канал, но это тоже не работает.

5
ответ дан 23 November 2019 в 23:05
поделиться

Как сказал JT, NIO - лучшее (и правильное) решение. Однако если вы действительно застряли с InputStream, вы можете либо

  1. порождать поток, единственной задачей которого является чтение из InputStream, и поместить результат в буфер, который можно прочитать из вашего исходного потока без блокировки. Это должно работать хорошо, если у вас есть только один экземпляр потока. В противном случае вы можете убить поток, используя устаревшие методы в классе Thread, хотя это может вызвать утечку ресурсов.

  2. Полагайтесь на isAvailable, чтобы указать данные, которые можно прочитать без блокировки. Однако в некоторых случаях (например, с помощью сокетов) может потребоваться потенциально блокирующее чтение для isAvailable, чтобы сообщить о чем-то отличном от 0.

4
ответ дан 23 November 2019 в 23:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: