BufferedReader.readLine()有时挂起

时间:2017-04-07 13:26:50

标签: java apache rss httpclient bufferedreader

在我的应用程序中,有一个单独的线程,每分钟由ScheduledExecutorService.scheduleAtFixedRate()运行,它解析来自多个网站的RSS源。我正在使用Apache HttpClient接收xml。

示例代码:

InputStream inputStream = HTTPClient.get(url);    
String xml = inputStreamToString(inputStream, encoding, websiteName);

public static String inputStreamToString(InputStream inputStream, String encoding, String websiteName) 
{

    BufferedReader bufferedReader = null;
    PrintWriter printWriter = null;
    StringBuilder stringBuilder = new StringBuilder();

    int letter;
    try 
    {
        bufferedReader = new BufferedReader(new InputStreamReader(inputStream, encoding));
        printWriter = new PrintWriter(new File("src/doclog/" 
                + websiteName + "_" 
                + new SimpleDateFormat("MM_dd_yyyy_hh_mm_ss").format(new Date(System.currentTimeMillis())) 
                + "_" + encoding + ".txt"), encoding);
        while((letter = bufferedReader.read()) != -1) 
        {
            char character = (char) letter;
            printWriter.print(character);               
            stringBuilder.append(character);
        }
    } 
    catch(IOException e) 
    {
        throw new RuntimeException(e);
    } 
    finally
    {
        try 
        {
            if(bufferedReader != null) 
            {
                bufferedReader.close();
            }
            if(printWriter != null) 
            {
                printWriter.close();
            }
        } 
        catch(IOException e) 
        {
            e.printStackTrace();
        }
    }
    System.out.println("String built");
    return stringBuilder.toString();
}

和HTTPClient类:

public class HTTPClient 
{
   private static final HttpClient CLIENT = HttpClientBuilder.create().build(); 

   public static InputStream get(String url)
   {    
       try
       {
           HttpGet request = new HttpGet(url);  
           HttpResponse response = CLIENT.execute(request);
           System.out.println("Response Code: " + response.getStatusLine().toString()); 
           return response.getEntity().getContent();
       }
       catch(IOException | IllegalArgumentException e)
       {
           throw new RuntimeException(e);
       }
   }
}

正如标题所说,有时bufferedReader.readLine()有可能永远挂起。我在这个主题上看到了另一个答案,他们建议检查bufferedReader.ready()是否返回true。问题是有些网站会在false中一直返回bufferedReader.ready(),同时处理它们,但是它们解析得很好。

如何防止我的线程挂在bufferedReader.readLine()上?

如果重要,response.getStatusLine().toString()始终会返回HTTP/1.1 200 OK

修改

我发现挂起时bufferedReader.ready()实际上是true

编辑2

BufferedReader.read()也挂了。奇怪的是,只有在处理一个网站时才会发生挂起,并且它的发生绝对是随机的。应用程序可以工作15个小时,接收数百个无问题的响应,或者在发布后10分钟内挂起。我已经开始将每个更新的所有字符写入单独的文件中,并发现没有什么特别的事情真的发生。 Xml读取只是在文档中间永远停止,最后一个字符是<p dir="ltr"&g。更新了代码。

另外,值得注意的是,不能有任何未处理的异常,因为在我的ScheduledExecutorService.scheduleAtFixedRate()可运行的最高级别我捕获Throwable,并打印它的stackTrace。

1 个答案:

答案 0 :(得分:1)

ready()方法返回true,告诉您有可供阅读的字符。问题是readLine()阻塞,直到它在输入中找到行尾。

  

public String readLine()                   抛出IOException

     

读取一行文字。一条线被认为是任何一条线终止的   换行(' \ n'),回车(' \ r')或回车   然后立即换行。

当您从流中读取时,无法保证数据将进入行边界,因此readLine()调用阻止。

您可以使用不会阻止的read方法,但您必须自己检查EOL。

  

public int read(char [] cbuf,int off,int len)抛出IOException

     

将字符读入数组的一部分。

     

此方法实现相应读取的一般合约   Reader类的方法。作为额外的便利,它尝试   通过反复调用read来读取尽可能多的字符   底层流的方法。这种迭代读取一直持续到   以下条件之一成为现实:

The specified number of characters have been read,
The read method of the underlying stream returns -1, indicating end-of-file, or
The ready method of the underlying stream returns false, indicating that further input requests would block. 
     

如果基础流上的第一次读取返回-1表示   end-of-file然后此方法返回-1。否则此方法返回   实际读取的字符数。

此外,您还必须从读取的字符重建线条。一次读取整行是不方便的,但必须这样做。