【问题标题】:PrintWriter.print blocks when the string is too long当字符串太长时 PrintWriter.print 阻塞
【发布时间】:2015-10-15 17:40:09
【问题描述】:

我正在用 java 编写一个包装程序,它只是应该通过在流中写入其标准并从其标准输出流中读取响应来将参数传递给其他进程。但是,当我尝试传入的String 太大时,PrintWriter.print 只会阻塞。没有错误,只是冻结。有没有好的解决方法?

相关代码

public class Wrapper {
    PrintWriter writer;
    
    public Wrapper(String command){
        start(command);
    }

    public void call(String args){
        writer.println(args); // Blocks here
        writer.flush();
        
        //Other code
    }

    public void start(String command) {
        try {
            ProcessBuilder pb = new ProcessBuilder(command.split(" "));
            pb.redirectErrorStream(true);
            process = pb.start();
            // STDIN of the process.
            writer = new PrintWriter(new OutputStreamWriter(process.getOutputStream(), "UTF-8"));
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Process ended catastrophically.");
        }
    }
    
    
}

如果我尝试使用

writer.print(args);
writer.print("\n");

它可以在冻结之前处理更大的字符串,但最终仍会锁定。 是否有缓冲流的方法来解决这个问题? print 是否阻塞了具有足够空间的进程流?

更新

针对一些答案和 cmets,我提供了更多信息。

  • 操作系统为 Windows 7
  • BufferedWriter 会减慢运行时间,但最终并没有阻止它阻塞。
  • 字符串可能会变得很长,长达 100,000 个字符
  • 进程输入被消耗,但按行,即 Scanner.nextLine();

测试代码

import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;

import ProcessRunner.Wrapper;


public class test {

    public static void main(String[] args){
        System.out.println("Building...");
        Wrapper w = new Wrapper("java echo");
        System.out.println("Calling...");
        String market = "aaaaaa";
        for(int i = 0; i < 1000; i++){
            try {
                System.out.println(w.call(market, 1000));
            } catch (InterruptedException | ExecutionException
                    | TimeoutException e) {
                
                System.out.println("Timed out");
            }
            market = market + market;
            System.out.println("Size = " + market.length());
        }
        System.out.println("Stopping...");
        try {
            w.stop();
        } catch (IOException e) {
            
            e.printStackTrace();
            System.out.println("Stop failed :(");
        }
    }
}

测试过程:

您必须先编译此文件,并确保 .class 与测试 .class 文件位于同一文件夹中

import java.util.Scanner;


public class echo {
    
    public static void main(String[] args){
            while(true){
            Scanner stdIn = new Scanner(System.in);
            System.out.println(stdIn.nextLine());
            }
    }

}

【问题讨论】:

  • 你似乎知道缓冲,所以你为什么不尝试使用BufferedWriter
  • @Dici 我不太确定如何从 String 对象转到 BufferedWriter,也不确定这是否能解决问题,所以一直在努力解决这个问题,直到我明白了一点确认或指导。你觉得这样能解决问题吗?
  • @Cain 毕竟BufferedWriter 的API 是documented
  • 我想尝试重现该问题。问:什么是操作系统(Linux?Windows?其他?) 问:字符串有多长? 100个字? 1000个? 64,000 个字符(或更长)? ALSO:坦率地说,认为尝试缓冲写入器是个好主意。
  • @EJP 我确实尝试过 BufferedWriter,但运行时间较慢,仍然被阻塞

标签: java printwriter


【解决方案1】:

如果对等方的读取速度比您正在写入的速度慢,则以任何方式写入 java.io 中的任何管道或套接字会阻塞。

对此你无能为力。

【讨论】:

  • 你可以在一个单独的线程中进行写入,这样至少只有那个线程被阻塞,而不是你的整个应用程序。
  • 慢一点很好,但在某些时候这只是无限期地阻塞
【解决方案2】:

我怀疑这里发生的事情是外部进程正在写入其标准输出。由于您的 Java 代码不会读取它,因此它最终会填充外部进程的标准输出(或错误)管道。这会阻塞外部进程,这意味着它可以从其输入管道中读取......并且您的 Java 进程会冻结。


如果这是问题所在,那么使用缓冲写入器将无法解决问题。您要么需要读取外部进程的输出,要么将其重定向到文件(例如 Linux 上的“/dev/null”)

【讨论】:

  • 另一个进程是逐行读取。但是如果字符串太大,是否会截断行尾字符?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多