क्या एक टाइमआउट के साथ एक इनपुटस्ट्रीम से पढ़ना संभव है?


147

विशेष रूप से, समस्या इस तरह से एक विधि लिखने की है:

int maybeRead(InputStream in, long timeout)

जहाँ रिटर्न मान in.read () के समान है यदि डेटा 'टाइमआउट' मिलीसेकंड के भीतर उपलब्ध है, और -2 अन्यथा। विधि लौटने से पहले, किसी भी थ्रेडेड धागे से बाहर निकलना चाहिए।

तर्कों से बचने के लिए, यहाँ विषय java.io.InputStream, सन द्वारा प्रलेखित (किसी भी जावा संस्करण) के रूप में। कृपया ध्यान दें कि यह उतना सरल नहीं है जितना दिखता है। नीचे कुछ तथ्य दिए गए हैं जो सीधे सूर्य के प्रलेखन द्वारा समर्थित हैं।

  1. In.read () विधि गैर-रुकावट हो सकती है।

  2. किसी रीडर या InterruptibleChannel में InputStream रैप करना मदद नहीं करता है, क्योंकि वे सभी क्लासेस इनपुटस्ट्रीम की कॉल विधियाँ कर सकते हैं। यदि उन कक्षाओं का उपयोग करना संभव था, तो एक समाधान लिखना संभव होगा जो कि सीधे उसी तर्क को इनपुटस्ट्रीम पर निष्पादित करता है।

  3. यह हमेशा। के लिए स्वीकार्य है () 0 वापस करने के लिए।

  4. In.close () विधि ब्लॉक या कुछ भी नहीं कर सकती है।

  5. दूसरे धागे को मारने का कोई सामान्य तरीका नहीं है।

जवाबों:


83

InputStream.available () का उपयोग करना

यह हमेशा 0 वापस करने के लिए System.in.available () के लिए स्वीकार्य है।

मैंने इसके विपरीत पाया है - यह हमेशा उपलब्ध बाइट्स की संख्या के लिए सर्वोत्तम मूल्य देता है। के लिए Javadoc 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.

समय / गतिहीनता के कारण एक अनुमान अपरिहार्य है। आंकड़ा एकबारगी कम हो सकता है क्योंकि नए डेटा लगातार आ रहे हैं। हालांकि यह हमेशा अगली कॉल पर "कैच अप" करता है - यह सभी आये हुए डेटा के लिए खाता होना चाहिए, बार जो कि नए कॉल के क्षण में आता है। स्थायी रूप से 0 लौटने पर जब डेटा ऊपर की स्थिति में विफल रहता है।

पहला कैविट: इनपुटस्ट्रीम के ठोस उपवर्ग उपलब्ध हैं () के लिए जिम्मेदार हैं

InputStreamएक अमूर्त वर्ग है। इसका कोई डेटा स्रोत नहीं है। यह उपलब्ध डेटा के लिए अर्थहीन है। इसलिए, javadoc available()भी बताता है:

The available method for class InputStream always returns 0.

This method should be overridden by subclasses.

और वास्तव में, कंक्रीट इनपुट स्ट्रीम क्लासेस ओवरराइड करते हैं (), सार्थक मान प्रदान करते हुए, निरंतर 0s नहीं।

दूसरा कैविएट: सुनिश्चित करें कि आप विंडोज में इनपुट टाइप करते समय गाड़ी-वापसी का उपयोग करें।

यदि उपयोग करते हैं System.in, तो आपका प्रोग्राम केवल इनपुट प्राप्त करता है जब आपका कमांड शेल इसे सौंप देता है। यदि आप फ़ाइल पुनर्निर्देशन / पाइप (जैसे somefile> java myJavaApp या somecommand। Java myJavaApp) का उपयोग कर रहे हैं, तो इनपुट डेटा आमतौर पर तुरंत सौंप दिया जाता है। हालांकि, यदि आप मैन्युअल रूप से इनपुट टाइप करते हैं, तो डेटा हैंडओवर में देरी हो सकती है। उदाहरण के लिए विंडोज़ cmd.exe शेल के साथ, डेटा cmd.exe शेल में बफ़र किए जाते हैं। डेटा केवल कैरिज-रिटर्न (नियंत्रण-एम या <enter>) के बाद निष्पादित जावा प्रोग्राम को दिया जाता है । यह निष्पादन पर्यावरण की एक सीमा है। बेशक, InputStream.available () शेल बफ़र्स को डेटा के रूप में लंबे समय तक 0 लौटाएगा - यह सही व्यवहार है; उस बिंदु पर कोई उपलब्ध डेटा नहीं हैं। जैसे ही डेटा शेल से उपलब्ध होता है, विधि एक मान> 0. NB: Cygwin cmd का उपयोग करती है।

सबसे सरल समाधान (कोई अवरुद्ध नहीं है, इसलिए कोई समय समाप्ति की आवश्यकता नहीं है)

बस इस का उपयोग करें:

    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.

या समकक्ष,

    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);
         }

अमीर समाधान (अधिकतम समय-समय पर बफर भरता है)

इसे घोषित करें:

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;
 }

तो इस का उपयोग करें:

    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.

1
यदि is.available() > 1024यह सुझाव विफल हो जाएगा। निश्चित रूप से ऐसी धाराएँ हैं जो शून्य पर लौटती हैं। उदाहरण के लिए SSLSockets हाल ही में। आप इस पर भरोसा नहीं कर सकते।
लोर्न

मामला 'is.available ()> 1024' विशेष रूप से readLength से निपटा जाता है।
ग्लेन बेस्ट

टिप्पणी पुनः SSLSockets गलत - यह उपलब्ध iff के लिए 0 देता है यदि बफर में कोई डेटा नहीं है। मेरे जवाब के अनुसार। Javadoc: "यदि सॉकेट पर बाइट्स नहीं हैं, और सॉकेट को बंद करके बंद नहीं किया गया है, तो उपलब्ध 0. वापस आ जाएगा"
Glen Best

@GlenBest मेरी टिप्पणी पुनः SSLSocket गलत नहीं है। हाल तक [मेरा जोर] यह हर समय शून्य पर लौटता था। आप वर्तमान की बात कर रहे हैं। मैं JSSE के पूरे इतिहास के बारे में बात कर रहा हूं, और 2002 में जावा 1.4 में शामिल होने से पहले मैंने इसके साथ काम किया है ।
लोरने का मार्किस

लूप की शर्तों को बदलकर "जबकि (is.available ()> 0 && System.currentTimeMillis () <maxTimeMillis && बफरऑफ़सेट <b.length) {" ने मुझे CPU ओवरहेड का एक टन बचाया।
लॉजिक

65

मान लें कि आपकी धारा सॉकेट द्वारा समर्थित नहीं है (इसलिए आप उपयोग नहीं कर सकते हैं Socket.setSoTimeout()), मुझे लगता है कि इस प्रकार की समस्या को हल करने का मानक तरीका फ्यूचर का उपयोग करना है।

मान लें कि मेरे पास निम्नलिखित निष्पादक और धाराएँ हैं:

    ExecutorService executor = Executors.newFixedThreadPool(2);
    final PipedOutputStream outputStream = new PipedOutputStream();
    final PipedInputStream inputStream = new PipedInputStream(outputStream);

मेरे पास लेखक है जो कुछ डेटा लिखता है फिर डेटा के अंतिम टुकड़े को लिखने और धारा को बंद करने से पहले 5 सेकंड तक प्रतीक्षा करता है:

    Runnable writeTask = new Runnable() {
        @Override
        public void run() {
            try {
                outputStream.write(1);
                outputStream.write(2);
                Thread.sleep(5000);
                outputStream.write(3);
                outputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };
    executor.submit(writeTask);

इसे पढ़ने का सामान्य तरीका इस प्रकार है। रीड डेटा के लिए अनिश्चित काल के लिए ब्लॉक करेगा और इसलिए यह 5 एस में पूरा होता है:

    long start = currentTimeMillis();
    int readByte = 1;
    // Read data without timeout
    while (readByte >= 0) {
        readByte = inputStream.read();
        if (readByte >= 0)
            System.out.println("Read: " + readByte);
    }
    System.out.println("Complete in " + (currentTimeMillis() - start) + "ms");

कौन से आउटपुट:

Read: 1
Read: 2
Read: 3
Complete in 5001ms

अगर कोई अधिक मौलिक समस्या थी, जैसे कि लेखक जवाब नहीं दे रहा है, तो पाठक हमेशा के लिए अवरुद्ध हो जाएगा। अगर मैं भविष्य में रीड को लपेटता हूं, तो मैं टाइमआउट को इस प्रकार नियंत्रित कर सकता हूं:

    int readByte = 1;
    // Read data with timeout
    Callable<Integer> readTask = new Callable<Integer>() {
        @Override
        public Integer call() throws Exception {
            return inputStream.read();
        }
    };
    while (readByte >= 0) {
        Future<Integer> future = executor.submit(readTask);
        readByte = future.get(1000, TimeUnit.MILLISECONDS);
        if (readByte >= 0)
            System.out.println("Read: " + readByte);
    }

कौन से आउटपुट:

Read: 1
Read: 2
Exception in thread "main" java.util.concurrent.TimeoutException
    at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:228)
    at java.util.concurrent.FutureTask.get(FutureTask.java:91)
    at test.InputStreamWithTimeoutTest.main(InputStreamWithTimeoutTest.java:74)

मैं टाइमआउट एक्ससेप्शन को पकड़ सकता हूं और मुझे जो भी सफाई चाहिए वह कर सकता हूं।


14
लेकिन अवरुद्ध धागे के बारे में क्या?! क्या यह तब तक मेमोरी में रहेगा जब तक एप्लिकेशन समाप्त नहीं हो जाता? यदि मैं सही हूं, तो यह अंतहीन थ्रेड्स का उत्पादन कर सकता है आवेदन भारी भरा हुआ है और इससे भी अधिक, अपने पूल का उपयोग करने से आगे के थ्रेड्स को ब्लॉक करें जिसमें यह थ्रेड्स कब्जा है और अवरुद्ध है। कृपया मुझे सुधारें अगर मैं गलत हूं। धन्यवाद।
मुहम्मद गब्बाना

4
मुहम्मद गालबाना, आप सही हैं: ब्लॉकिंग रीड () धागा चलता रहता है और यह ठीक नहीं है। मुझे हालांकि इसे रोकने का एक तरीका मिल गया है: जब टाइमआउट हिट होता है, तो कॉलिंग थ्रेड इनपुट स्ट्रीम से बंद होता है (मेरे मामले में मैं एंड्रॉइड ब्लूटूथ सॉकेट बंद करता हूं जहां से इनपुट स्ट्रीम आती है)। जब आप ऐसा करते हैं, तो रीड () कॉल तुरंत वापस आ जाएगी .. वैसे मेरे मामले में मैं इंट रीड (बाइट []) ओवरलोड का उपयोग करता हूं, और वह तुरंत वापस आ जाता है। शायद इंट रीड () ओवरलोड एक IOException को फेंक देगा क्योंकि मुझे नहीं पता कि यह क्या लौटेगा ... मेरे दिमाग में कि उचित समाधान है।
इमैनुएल टाउज़री 11

5
-1 के रूप में थ्रेड्स पढ़ना तब तक अवरुद्ध रहता है जब तक कि एप्लिकेशन समाप्त न हो जाए।
Ortwin Angermeier

11
@ortang "मैं टाइमआउट एक्ससेप्शन को पकड़ता हूं और जो कुछ भी सफाई करता है, उसका मतलब है" ... उदाहरण के लिए, मैं पढ़ने वाले धागे को मारना चाहता हूं: ... कैच (टाइमआउट एक्ससेप्शन ई) {निष्पादक.नोटडाउन नाउ (); }
इयान जोन्स

12
executer.shutdownNowधागा नहीं मारेगा। यह बिना किसी प्रभाव के इसे बाधित करने की कोशिश करेगा। कोई सफाई संभव नहीं है और यह एक गंभीर मुद्दा है।
मार्को टोपोलनिक

22

यदि आपका InputStream सॉकेट द्वारा समर्थित है, तो आप setSTimeTime का उपयोग करके सॉकेट टाइमआउट (मिलीसेकंड में) सेट कर सकते हैं । यदि रीडआउट () कॉल निर्दिष्ट टाइमआउट के भीतर अनब्लॉक नहीं होता है, तो यह सॉकेट टाइमआउट अपवाद को फेंक देगा।

बस सुनिश्चित करें कि आप रीड () कॉल करने से पहले सॉकेट पर setSoTimeout कॉल करते हैं।


18

मैं समस्या के बयान पर सवाल उठाने के बजाय सिर्फ आँख बंद करके स्वीकार करूंगा। आपको केवल कंसोल या नेटवर्क पर टाइमआउट की आवश्यकता है। यदि आपके पास बाद में है Socket.setSoTimeout()और HttpURLConnection.setReadTimeout()जो दोनों बिल्कुल वही करते हैं, जो आवश्यक है, जब तक आप उन्हें सही ढंग से सेट करते हैं जब आप उनका निर्माण / अधिग्रहण करते हैं। बाद में आवेदन में एक अनियंत्रित बिंदु पर छोड़ना जब आपके पास सब कुछ है इनपुटस्ट्रीम एक बहुत ही अजीब कार्यान्वयन के लिए खराब डिजाइन है।


10
ऐसी अन्य परिस्थितियां हैं जहां एक रीड संभावित समय के लिए ब्लॉक कर सकता है; जैसे कि टेप ड्राइव से पढ़ते समय, दूर से माउंटेड नेटवर्क ड्राइव से या पिछले छोर पर टेप रोबोट के साथ HFS से। (लेकिन आपके उत्तर का मुख्य जोर सही है।)
स्टीफन सी

1
आपकी टिप्पणी और उदाहरणों के लिए @StephenC +1। अपने उदाहरण को जोड़ने के लिए, एक साधारण मामला हो सकता है जहां सॉकेट कनेक्शन सही तरीके से बनाया गया था, लेकिन पढ़ने का प्रयास अवरुद्ध था क्योंकि डेटा को DB से प्राप्त किया जाना था, लेकिन यह किसी भी तरह से नहीं हुआ (चलो कहते हैं कि DB जवाब नहीं दे रहा था और गया था) बंद अवस्था में)। इस परिदृश्य में आपको सॉकेट पर रीड ऑपरेशन को स्पष्ट रूप से टाइमआउट करने का एक तरीका होना चाहिए।
13'13

1
इनपुटस्ट्रीम एब्स्ट्रैक्शन का पूरा बिंदु अंतर्निहित कार्यान्वयन के बारे में नहीं सोचना है। पोस्ट किए गए उत्तरों के पेशेवरों और विपक्षों के बारे में बहस करना उचित है। लेकिन, समस्या के बयान पर सवाल उठाने के लिए, उपद्रव में मदद करने के लिए नहीं जा रहा है
pellucide

2
InputStream एक स्ट्रीम पर काम करता है और यह ब्लॉक हो जाता है, फिर भी यह टाइमआउट तंत्र प्रदान नहीं करता है। तो InputStream अमूर्तता एक उपयुक्त डिजाइन अमूर्त नहीं है। इसलिए किसी स्ट्रीम पर टाइमआउट करने का तरीका नहीं पूछ रहा है। तो सवाल एक बहुत ही व्यावहारिक समस्या के समाधान के लिए पूछ रहा है। अधिकांश अंतर्निहित कार्यान्वयन अवरुद्ध हो जाएंगे। एक धारा का बहुत सार। यदि धारा का दूसरा पक्ष नए डेटा के साथ तैयार नहीं है तो सॉकेट्स, फाइल्स, पाइप्स ब्लॉक हो जाएंगे।
पेलुकाइड

2
@EJP। मुझे नहीं पता कि तुम्हें कैसे मिला। मैं आपसे सहमत नहीं था। समस्या का बयान "एक इनपुटस्ट्रीम पर टाइमआउट कैसे करें" मान्य है। चूँकि फ्रेमवर्क टाइमआउट का रास्ता प्रदान नहीं करता है, ऐसे प्रश्न पूछना उचित है।
pellucide

7

मैंने जावा एनआईओ पैकेज से कक्षाओं का उपयोग नहीं किया है, लेकिन ऐसा लगता है कि वे यहां कुछ मदद कर सकते हैं। विशेष रूप से, java.nio.channels.Channels और java.nio.chanelines.Interruptiblehanhannel


2
+1: मुझे विश्वास नहीं है कि ओपी इनपुटस्ट्रीम के साथ अकेले क्या कर रहा है, यह करने के लिए एक विश्वसनीय तरीका है। हालाँकि, इस उद्देश्य के लिए, अन्य लोगों के बीच nio बनाया गया था।
एडी

2
ओपी ने पहले ही मूल रूप से इस पर शासन किया है। InputStreams स्वाभाविक रूप से अवरुद्ध हैं और गैर-रुकावट हो सकते हैं।
लोर्ने

5

यहाँ System.in से एक NIO FileChannel प्राप्त करने और टाइमआउट का उपयोग करके डेटा की उपलब्धता की जांच करने का एक तरीका है, जो प्रश्न में वर्णित समस्या का एक विशेष मामला है। इसे कंसोल पर चलाएँ, कोई भी इनपुट टाइप न करें, और परिणाम की प्रतीक्षा करें। यह विंडोज और लिनक्स पर जावा 6 के तहत सफलतापूर्वक परीक्षण किया गया था।

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];
    }
}

दिलचस्प है, जब कंसोल के बजाय नेटबीन्स 6.5 के अंदर प्रोग्राम चलाते हैं, तो टाइमआउट बिल्कुल भी काम नहीं करता है, और सिस्टम थ्रेडिट () को कॉल करना वास्तव में ज़ोंबी थ्रेड्स को मारने के लिए आवश्यक है। क्या होता है कि पाठक को कॉल पर अवरोधक थ्रेड ब्लॉक (!)। एक अन्य परीक्षण कार्यक्रम (यहां नहीं दिखाया गया है) अतिरिक्त रूप से चैनल को बंद करने की कोशिश करता है, लेकिन यह भी काम नहीं करता है।


मैक ओएस पर काम नहीं करता है, न तो JDK 1.6 के साथ और न ही JDK 1.7 के साथ। रीड के दौरान रिटर्न को दबाने के बाद ही रुकावट को पहचाना जाता है।
मोस्टोव्स्की को

4

जैसा कि jt ने कहा, NIO सबसे अच्छा (और सही) समाधान है। यदि आप वास्तव में एक इनपुटस्ट्रीम के साथ फंस गए हैं, तो आप या तो कर सकते हैं

  1. एक थ्रेड स्पॉन करें जो अनन्य कार्य इनपुटस्ट्रीम से पढ़ना है और परिणाम को एक बफर में डाल दिया है जिसे आपके मूल थ्रेड से अवरुद्ध होने के बाद पढ़ा जा सकता है। यदि आप कभी भी धारा का केवल एक उदाहरण है तो यह अच्छी तरह से काम करना चाहिए। अन्यथा आप थ्रेड वर्ग में पदावनत विधियों का उपयोग करके थ्रेड को मारने में सक्षम हो सकते हैं, हालांकि इससे रिसोर्स लीक हो सकता है।

  2. उस डेटा को इंगित करने के लिए isAvailable पर निर्भर है जिसे ब्लॉक किए बिना पढ़ा जा सकता है। हालाँकि कुछ मामलों में (जैसे कि सॉकेट्स के लिए) यह 0 के अलावा किसी अन्य चीज़ की रिपोर्ट करने के लिए उपलब्ध होने के लिए संभावित रूप से अवरुद्ध रीडिंग ले सकता है।


5
Socket.setSoTimeout()एक समान रूप से सही और ज्यादा सरल समाधान है। या HttpURLConnection.setReadTimeout()
लोर्ने

3
@ ईजेपी - ये केवल कुछ परिस्थितियों में "समान रूप से सही" हैं; उदाहरण के लिए यदि इनपुट स्ट्रीम सॉकेट स्ट्रीम / HTTP कनेक्शन स्ट्रीम है।
स्टीफन सी

1
@Stephen C NIO केवल एक ही परिस्थितियों में गैर-अवरुद्ध और चयन योग्य है। उदाहरण के लिए कोई गैर-अवरोधक फ़ाइल I / O नहीं है।
लोर्न

2
@EJP लेकिन नॉन-ब्लॉकिंग पाइप IO (System.in), नॉन-ब्लॉकिंग I / O फाइल के लिए (स्थानीय डिस्क पर) बकवास है
woky

1
@EJP अधिकांश पर (सभी?) Unices System.in वास्तव में एक पाइप है (यदि आपने शेल को फ़ाइल के साथ बदलने के लिए नहीं कहा था) और पाइप के रूप में यह गैर-अवरुद्ध हो सकता है।
21

0

इस उत्तर से प्रेरित होकर मैं कुछ और वस्तु-उन्मुख समाधान लेकर आया।

यह तभी मान्य है जब आप वर्ण पढ़ने का इरादा कर रहे हैं

आप BufferedReader को ओवरराइड कर सकते हैं और कुछ इस तरह से लागू कर सकते हैं:

public class SafeBufferedReader extends BufferedReader{

    private long millisTimeout;

    ( . . . )

    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
        try {
            waitReady();
        } catch(IllegalThreadStateException e) {
            return 0;
        }
        return super.read(cbuf, off, len);
    }

    protected void waitReady() throws IllegalThreadStateException, IOException {
        if(ready()) return;
        long timeout = System.currentTimeMillis() + millisTimeout;
        while(System.currentTimeMillis() < timeout) {
            if(ready()) return;
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                break; // Should restore flag
            }
        }
        if(ready()) return; // Just in case.
        throw new IllegalThreadStateException("Read timed out");
    }
}

यहाँ लगभग पूर्ण उदाहरण है।

मैं कुछ तरीकों पर 0 वापस कर रहा हूं, आपको अपनी आवश्यकताओं को पूरा करने के लिए इसे -2 में बदलना चाहिए, लेकिन मुझे लगता है कि 0 बफ़रड्रेडर अनुबंध के साथ अधिक उपयुक्त है। कुछ भी गलत नहीं हुआ, यह सिर्फ 0 वर्ण पढ़ा। readLine विधि एक भयानक प्रदर्शन हत्यारा है। यदि आप वास्तव में रीडलाइनका उपयोग करना चाहते हैं तो आपको एक पूरी तरह से नया बफ़रडियर बनाना चाहिए । अभी, यह धागा सुरक्षित नहीं है। यदि कोई रीडलाइन के दौरान किसी ऑपरेशन का आह्वान करता है तो लाइन का इंतजार कर रहा है, यह अप्रत्याशित परिणाम देगा

मुझे -2 वापस आना पसंद नहीं है जहां मैं हूं। मैं एक अपवाद फेंक सकता हूं क्योंकि कुछ लोग बस जाँच कर सकते हैं यदि ईओ पर विचार करने के लिए int <0। वैसे भी, उन तरीकों का दावा है कि "ब्लॉक नहीं कर सकते हैं", आपको जांचना चाहिए कि क्या यह कथन वास्तव में सच है और बस ओवरराइड नहीं है।

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.nio.CharBuffer;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;

/**
 * 
 * readLine
 * 
 * @author Dario
 *
 */
public class SafeBufferedReader extends BufferedReader{

    private long millisTimeout;

    private long millisInterval = 100;

    private int lookAheadLine;

    public SafeBufferedReader(Reader in, int sz, long millisTimeout) {
        super(in, sz);
        this.millisTimeout = millisTimeout;
    }

    public SafeBufferedReader(Reader in, long millisTimeout) {
        super(in);
        this.millisTimeout = millisTimeout;
    }



    /**
     * This is probably going to kill readLine performance. You should study BufferedReader and completly override the method.
     * 
     * It should mark the position, then perform its normal operation in a nonblocking way, and if it reaches the timeout then reset position and throw IllegalThreadStateException
     * 
     */
    @Override
    public String readLine() throws IOException {
        try {
            waitReadyLine();
        } catch(IllegalThreadStateException e) {
            //return null; //Null usually means EOS here, so we can't.
            throw e;
        }
        return super.readLine();
    }

    @Override
    public int read() throws IOException {
        try {
            waitReady();
        } catch(IllegalThreadStateException e) {
            return -2; // I'd throw a runtime here, as some people may just be checking if int < 0 to consider EOS
        }
        return super.read();
    }

    @Override
    public int read(char[] cbuf) throws IOException {
        try {
            waitReady();
        } catch(IllegalThreadStateException e) {
            return -2;  // I'd throw a runtime here, as some people may just be checking if int < 0 to consider EOS
        }
        return super.read(cbuf);
    }

    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
        try {
            waitReady();
        } catch(IllegalThreadStateException e) {
            return 0;
        }
        return super.read(cbuf, off, len);
    }

    @Override
    public int read(CharBuffer target) throws IOException {
        try {
            waitReady();
        } catch(IllegalThreadStateException e) {
            return 0;
        }
        return super.read(target);
    }

    @Override
    public void mark(int readAheadLimit) throws IOException {
        super.mark(readAheadLimit);
    }

    @Override
    public Stream<String> lines() {
        return super.lines();
    }

    @Override
    public void reset() throws IOException {
        super.reset();
    }

    @Override
    public long skip(long n) throws IOException {
        return super.skip(n);
    }

    public long getMillisTimeout() {
        return millisTimeout;
    }

    public void setMillisTimeout(long millisTimeout) {
        this.millisTimeout = millisTimeout;
    }

    public void setTimeout(long timeout, TimeUnit unit) {
        this.millisTimeout = TimeUnit.MILLISECONDS.convert(timeout, unit);
    }

    public long getMillisInterval() {
        return millisInterval;
    }

    public void setMillisInterval(long millisInterval) {
        this.millisInterval = millisInterval;
    }

    public void setInterval(long time, TimeUnit unit) {
        this.millisInterval = TimeUnit.MILLISECONDS.convert(time, unit);
    }

    /**
     * This is actually forcing us to read the buffer twice in order to determine a line is actually ready.
     * 
     * @throws IllegalThreadStateException
     * @throws IOException
     */
    protected void waitReadyLine() throws IllegalThreadStateException, IOException {
        long timeout = System.currentTimeMillis() + millisTimeout;
        waitReady();

        super.mark(lookAheadLine);
        try {
            while(System.currentTimeMillis() < timeout) {
                while(ready()) {
                    int charInt = super.read();
                    if(charInt==-1) return; // EOS reached
                    char character = (char) charInt;
                    if(character == '\n' || character == '\r' ) return;
                }
                try {
                    Thread.sleep(millisInterval);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt(); // Restore flag
                    break;
                }
            }
        } finally {
            super.reset();
        }
        throw new IllegalThreadStateException("readLine timed out");

    }

    protected void waitReady() throws IllegalThreadStateException, IOException {
        if(ready()) return;
        long timeout = System.currentTimeMillis() + millisTimeout;
        while(System.currentTimeMillis() < timeout) {
            if(ready()) return;
            try {
                Thread.sleep(millisInterval);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); // Restore flag
                break;
            }
        }
        if(ready()) return; // Just in case.
        throw new IllegalThreadStateException("read timed out");
    }

}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.