Java 通过多线程访问文件
我想通过10个线程访问一个大文件(文件大小可能从30 MB到1 GB不等),然后处理文件中的每一行,并通过10个线程将它们写入另一个文件。如果我只使用一个线程访问IO,其他线程将被阻塞。处理所需的时间几乎相当于从文件系统中读取一行代码。还有一个约束,输出文件中的数据应与输入文件中的数据顺序相同 我想听听你对这个系统设计的想法。是否有任何现有API支持对文件的并发访问 同时,写入同一文件可能会导致死锁Java 通过多线程访问文件,java,file,concurrency,Java,File,Concurrency,我想通过10个线程访问一个大文件(文件大小可能从30 MB到1 GB不等),然后处理文件中的每一行,并通过10个线程将它们写入另一个文件。如果我只使用一个线程访问IO,其他线程将被阻塞。处理所需的时间几乎相当于从文件系统中读取一行代码。还有一个约束,输出文件中的数据应与输入文件中的数据顺序相同 我想听听你对这个系统设计的想法。是否有任何现有API支持对文件的并发访问 同时,写入同一文件可能会导致死锁 如果我关心时间限制,请建议如何实现这一点。可能的方法之一是创建一个线程,该线程将读取输入文件并将
如果我关心时间限制,请建议如何实现这一点。可能的方法之一是创建一个线程,该线程将读取输入文件并将读取行放入阻塞队列。几个线程将等待此队列中的数据,并处理数据 另一种可能的解决方案是将文件分成块,并将每个块分配给单独的线程
为了避免阻塞,可以使用异步IO。您还可以从中查看Proactor模式。我以前遇到过类似的情况,我处理它的方式如下: 逐行读取主线程中的文件,并将该行的处理提交给执行器。一个合理的起点如果您计划使用固定数量的线程,您可能会对
Executors.newFixedThreadPool(10)
Executors类中的工厂方法感兴趣。也不错
基本上,我会提交所有作业,调用shutdown,然后在主线程中继续按照返回的所有未来
的顺序写入输出文件。您可以利用Future
class'get()
方法的阻塞性质来确保顺序,但您确实不应该使用多线程来编写,就像您不应该使用它来读取一样。有道理吗
但是,1GB
数据文件?如果我是你,我首先会对有意义地分解这些文件感兴趣
PS:我故意避免回答中的代码,因为我想让OP自己试试。已经提供了足够多的指向特定类、API方法和示例的指针。我将从三个线程开始
- 您应该从文件读取中提取。创建一个类,该类读取文件并将内容分派给不同数量的线程
行中。G行号,因为您希望保留原始序列
- 您需要一个处理类,该类对收集的数据执行实际工作。就你而言,没有工作可做。该类只存储信息,您可以在某一天对其进行扩展以执行其他操作(例如,反转字符串。附加一些其他字符串,…)
- 然后需要一个合并类,该类对处理线程执行某种多路合并排序,并按顺序收集对
行
实例的所有引用
合并类也可以将数据写回文件,但为了保持代码干净
- 我建议创建一个输出类,再次从所有文件处理和内容中抽象出
当然,如果主内存不足,这种方法需要大量内存。您需要一种基于流的方法,这种方法可以将内存开销保持在较小的范围内
更新基于流的方法
一切都是一样的,除了:
读取器
线程将读取的数据泵入气球
。此引出序号可以容纳一定数量的行
实例(数量越大,消耗的主内存越多)
处理线程从引出序号中提取行
s,读取器在引出序号变空时将更多行泵入引出序号
合并类如上所述从处理线程获取行,编写器将数据写回文件
也许您应该在I/O线程中使用FileChannel
,因为它更适合读取大文件,并且在处理文件时可能消耗更少的内存(但这只是一个估计值)。因为需要维护顺序,所以问题本身就表明,读写不能并行进行,因为这是一个连续的过程,唯一可以并行进行的是记录处理,但这也不能用一个编写器解决很多问题
以下是设计方案:
使用一个线程t1读取文件并将数据存储到LinkedBlockingQueue Q1中
使用另一个线程t2从Q1读取数据并放入另一个LinkedBlockingQueue Q2
线程t3从Q2读取数据并写入文件
为了确保不会遇到OutofMemoryError,应该使用适当的大小初始化队列
YouC
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
public class OpenFile implements Runnable
{
private FileChannel _channel;
private FileChannel _writeChannel;
private int _startLocation;
private int _size;
public OpenFile(int loc, int sz, FileChannel chnl, FileChannel write)
{
_startLocation = loc;
_size = sz;
_channel = chnl;
_writeChannel = write;
}
public void run()
{
try
{
System.out.println("Reading the channel: " + _startLocation + ":" + _size);
ByteBuffer buff = ByteBuffer.allocate(_size);
if (_startLocation == 0)
Thread.sleep(100);
_channel.read(buff, _startLocation);
ByteBuffer wbuff = ByteBuffer.wrap(buff.array());
int written = _writeChannel.write(wbuff, _startLocation);
System.out.println("Read the channel: " + buff + ":" + new String(buff.array()) + ":Written:" + written);
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
throws Exception
{
FileOutputStream ostr = new FileOutputStream("OutBigFile.dat");
FileInputStream str = new FileInputStream("BigFile.dat");
String b = "Is this written";
//ostr.write(b.getBytes());
FileChannel chnl = str.getChannel();
FileChannel write = ostr.getChannel();
ByteBuffer buff = ByteBuffer.wrap(b.getBytes());
write.write(buff);
Thread t1 = new Thread(new OpenFile(0, 10000, chnl, write));
Thread t2 = new Thread(new OpenFile(10000, 10000, chnl, write));
Thread t3 = new Thread(new OpenFile(20000, 10000, chnl, write));
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
write.force(false);
str.close();
ostr.close();
}
}