[英]Read from a Java InputStream with very large amounts of data multiple times
我想知道从Java InputStream
多次读取字节的最佳方法是什么,并且当流非常大时仍然有效。 假设我有以下代码:
public void handleBytes(InputStream in) {
doStuff1(in);
doStuff2(in);
doStuff3(in);
}
doStuff1
, doStuff2
和doStuff3
都需要在相同的字节上工作,但做不同的事情。 我也假设这些函数可以是异步的。
我知道可以mark
然后reset
流,但我想知道这是否是in
有大量数据时的方法。 另外,如果我想每个doStuff-X
都有一个线程工作者,我真的不能使用reset
。
我应该为每个doStuff-X
方法获得流的副本吗? 但话又说回来,我不确定它会对大量数据有效。
您只能在不缓冲整个输入的情况下读取一次InputStream。
如果它是GB左右,您可以将其加载到内存中,或者将其复制到文件中并在有许多GB时重放它。 如果您可以在一个线程中解析数据,则可以将其传递给其他线程。
一般来说,这似乎是一个坏主意。 并不保证流完全支持mark
,即使支持mark
,也必须指定一个限制,在调用reset
之前可以读取多少字节。
既然你提到那些dostuff
可以异步运行,为什么不为每个dostuff
启动一个线程并使用队列同时将主线程的输入提供给这三个队列呢? 它需要一些同步,但这样您对输入音量没有限制,仍然可以限制内存使用。
如果您知道三个doStuff()函数是异步运行的,那么您可以尝试使用Apache Commons IO TeeInputStream将初始InputStream的内容复制到PipedOutputStream ,该PipedOutputStream连接到由doStuff2()读取的PipedInputStream 。 同样,您可以使用连接到doStuff3()的第二个PipedInputStream的第二个PipedOutputStream来设置第二个TeeInputStream。
这种方法有一些限制:
1)doStuff1(),doStuff2()和doStuff3()必须在不同的线程上运行,否则你将在doStuff1()运行时和doStuff2()和doStuff3()运行之前缓冲整个文件两次。 这种方法假设doStuff2()和doStuff3()正在读取和处理数据,而doStuff1()最初正在读取数据。
2)doStuff1()不能使用skip(),mark()或reset(),因为这会弄乱下游函数(如TeeInputStream javadoc中所述)。
只要所有三个doStuff()函数都能以大致相同的速率处理数据,这种方法应该具有合理的内存效率。
您可以采用PipedOutputStream和PipedInputStream。
static class Task extends Thread{
private final String taskName;
private final BufferedInputStream input;
public Task(String taskName, PipedInputStream input){
this.taskName = taskName;
this.input = new BufferedInputStream( input);
}
public void run(){
try {
System.out.println("Thread "+this.taskName+" Start");
final byte buf[] = new byte[8]; // 8 bytes for demo
while(true){
if( input.available() > 0){
input.read(buf);
System.out.println(String.format("Task Name %s, read:%s", this.taskName, new String(buf)));
}
else{
// TODO: Set break Condition:Ex: Check the expected read size
Thread.sleep(1000);
}
}
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public static void main(String args[]) {
try{
final PipedInputStream input1 = new PipedInputStream();
final PipedInputStream input2 = new PipedInputStream();
final PipedInputStream input3 = new PipedInputStream();
final Task t1 = new Task("Task1", input1);
final Task t2 = new Task("Task2", input2);
final Task t3 = new Task("Task3", input3);
t1.start();
t2.start();
t3.start();
Thread.sleep(300);
InputStream input = null;
try{
input = new FileInputStream("LargeInputFile.txt");
final PipedOutputStream out1 = new PipedOutputStream(input1);
final PipedOutputStream out2 = new PipedOutputStream(input2);
final PipedOutputStream out3 = new PipedOutputStream(input3);
byte buf[] = new byte[8]; // 8 bytes for demo
while(true){
if(input.available()>0){
int size = input.read(buf);
if(size > 0){
out1.write(buf);
out2.write(buf);
out3.write(buf);
out1.flush();
out2.flush();
out3.flush();
}
}
else{
System.out.println("Rread is finished!");
break;
}
}
}
finally{
if(input!=null){
input.close();
}
}
t1.join();
t2.join();
t3.join();
}
catch(Exception e){
e.printStackTrace(System.err);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.