Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/file/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 通过多线程访问文件_Java_File_Concurrency - Fatal编程技术网

Java 通过多线程访问文件

Java 通过多线程访问文件,java,file,concurrency,Java,File,Concurrency,我想通过10个线程访问一个大文件(文件大小可能从30 MB到1 GB不等),然后处理文件中的每一行,并通过10个线程将它们写入另一个文件。如果我只使用一个线程访问IO,其他线程将被阻塞。处理所需的时间几乎相当于从文件系统中读取一行代码。还有一个约束,输出文件中的数据应与输入文件中的数据顺序相同 我想听听你对这个系统设计的想法。是否有任何现有API支持对文件的并发访问 同时,写入同一文件可能会导致死锁 如果我关心时间限制,请建议如何实现这一点。可能的方法之一是创建一个线程,该线程将读取输入文件并将

我想通过10个线程访问一个大文件(文件大小可能从30 MB到1 GB不等),然后处理文件中的每一行,并通过10个线程将它们写入另一个文件。如果我只使用一个线程访问IO,其他线程将被阻塞。处理所需的时间几乎相当于从文件系统中读取一行代码。还有一个约束,输出文件中的数据应与输入文件中的数据顺序相同

我想听听你对这个系统设计的想法。是否有任何现有API支持对文件的并发访问

同时,写入同一文件可能会导致死锁


如果我关心时间限制,请建议如何实现这一点。

可能的方法之一是创建一个线程,该线程将读取输入文件并将读取行放入阻塞队列。几个线程将等待此队列中的数据,并处理数据

另一种可能的解决方案是将文件分成块,并将每个块分配给单独的线程


为了避免阻塞,可以使用异步IO。您还可以从

中查看Proactor模式。我以前遇到过类似的情况,我处理它的方式如下:

逐行读取主线程中的文件,并将该行的处理提交给执行器。一个合理的起点如果您计划使用固定数量的线程,您可能会对
Executors.newFixedThreadPool(10)
Executors类中的工厂方法感兴趣。也不错

基本上,我会提交所有作业,调用shutdown,然后在主线程中继续按照返回的所有
未来
的顺序写入输出文件。您可以利用
Future
class'
get()
方法的阻塞性质来确保顺序,但您确实不应该使用多线程来编写,就像您不应该使用它来读取一样。有道理吗

但是,
1GB
数据文件?如果我是你,我首先会对有意义地分解这些文件感兴趣


PS:我故意避免回答中的代码,因为我想让OP自己试试。已经提供了足够多的指向特定类、API方法和示例的指针。

我将从三个线程开始

  • 一个读线程,它读取数据,将数据分成“行”,并将它们放入一个有界阻塞队列(Q1)
  • 一个处理线程,从Q1读取数据,进行处理并将其放入第二个有界阻塞队列(Q2),然后
  • 从Q2读取数据并写入磁盘的写入线程 当然,我还要确保输出文件与输入文件位于物理上不同的磁盘上

    如果处理速度往往快于I/O(监控队列大小),那么您可以开始试验两个或多个并行“处理器”,它们在读写数据方面是同步的。

    • 您应该从文件读取中提取。创建一个类,该类读取文件并将内容分派给不同数量的线程
    类不应该分派字符串,它应该将它们包装在包含元信息的
    行中。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();
        }
    }