当前位置: 首页 > 面试题库 >

用Java的并行线程写入文件的最佳方法是什么?

唐威
2023-03-14
问题内容

我有一个程序,可以执行大量计算并经常将其报告到文件中。我知道频繁的写入操作会大大降低程序的速度,因此,为了避免出现这种情况,我希望有一个专门用于写入操作的线程。

现在,我正在用我写的这堂课(不耐烦的可以跳到问题的结尾):

public class ParallelWriter implements Runnable {

    private File file;
    private BlockingQueue<Item> q;
    private int indentation;

    public ParallelWriter( File f ){
        file = f;
        q = new LinkedBlockingQueue<Item>();
        indentation = 0;
    }

    public ParallelWriter append( CharSequence str ){
        try {
            CharSeqItem item = new CharSeqItem();
            item.content = str;
            item.type = ItemType.CHARSEQ;
            q.put(item);
            return this;
        } catch (InterruptedException ex) {
            throw new RuntimeException( ex );
        }
    }

    public ParallelWriter newLine(){
        try {
            Item item = new Item();
            item.type = ItemType.NEWLINE;
            q.put(item);
            return this;
        } catch (InterruptedException ex) {
            throw new RuntimeException( ex );
        }
    }

    public void setIndent(int indentation) {
        try{
            IndentCommand item = new IndentCommand();
            item.type = ItemType.INDENT;
            item.indent = indentation;
            q.put(item);
        } catch (InterruptedException ex) {
            throw new RuntimeException( ex );
        }
    }

    public void end(){
        try {
            Item item = new Item();
            item.type = ItemType.POISON;
            q.put(item);
        } catch (InterruptedException ex) {
            throw new RuntimeException( ex );
        }
    }

    public void run() {

        BufferedWriter out = null;
        Item item = null;

        try{
            out = new BufferedWriter( new FileWriter( file ) );
            while( (item = q.take()).type != ItemType.POISON ){
                switch( item.type ){
                    case NEWLINE:
                        out.newLine();
                        for( int i = 0; i < indentation; i++ )
                            out.append("   ");
                        break;
                    case INDENT:
                        indentation = ((IndentCommand)item).indent;
                        break;
                    case CHARSEQ:
                        out.append( ((CharSeqItem)item).content );
                }
            }
        } catch (InterruptedException ex){
            throw new RuntimeException( ex );
        } catch  (IOException ex) {
            throw new RuntimeException( ex );
        } finally {
            if( out != null ) try {
                out.close();
            } catch (IOException ex) {
                throw new RuntimeException( ex );
            }
        }
    }

    private enum ItemType {
        CHARSEQ, NEWLINE, INDENT, POISON;
    }
    private static class Item {
        ItemType type;
    }
    private static class CharSeqItem extends Item {
        CharSequence content;
    }
    private static class IndentCommand extends Item {
        int indent;
    }
}

然后我通过以下方式使用它:

ParallelWriter w = new ParallelWriter( myFile );
new Thread(w).start();

/// Lots of
w.append(" things ").newLine();
w.setIndent(2);
w.newLine().append(" more things ");

/// and finally
w.end();

尽管此方法运行良好,但我想知道: 是否有更好的方法来实现这一目标?


问题答案:

您的基本方法看起来不错。我将代码结构如下:

import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

public interface FileWriter {
    FileWriter append(CharSequence seq);

    FileWriter indent(int indent);

    void close();
}

class AsyncFileWriter implements FileWriter, Runnable {
    private final File file;
    private final Writer out;
    private final BlockingQueue<Item> queue = new LinkedBlockingQueue<Item>();
    private volatile boolean started = false;
    private volatile boolean stopped = false;

    public AsyncFileWriter(File file) throws IOException {
        this.file = file;
        this.out = new BufferedWriter(new java.io.FileWriter(file));
    }

    public FileWriter append(CharSequence seq) {
        if (!started) {
            throw new IllegalStateException("open() call expected before append()");
        }
        try {
            queue.put(new CharSeqItem(seq));
        } catch (InterruptedException ignored) {
        }
        return this;
    }

    public FileWriter indent(int indent) {
        if (!started) {
            throw new IllegalStateException("open() call expected before append()");
        }
        try {
            queue.put(new IndentItem(indent));
        } catch (InterruptedException ignored) {
        }
        return this;
    }

    public void open() {
        this.started = true;
        new Thread(this).start();
    }

    public void run() {
        while (!stopped) {
            try {
                Item item = queue.poll(100, TimeUnit.MICROSECONDS);
                if (item != null) {
                    try {
                        item.write(out);
                    } catch (IOException logme) {
                    }
                }
            } catch (InterruptedException e) {
            }
        }
        try {
            out.close();
        } catch (IOException ignore) {
        }
    }

    public void close() {
        this.stopped = true;
    }

    private static interface Item {
        void write(Writer out) throws IOException;
    }

    private static class CharSeqItem implements Item {
        private final CharSequence sequence;

        public CharSeqItem(CharSequence sequence) {
            this.sequence = sequence;
        }

        public void write(Writer out) throws IOException {
            out.append(sequence);
        }
    }

    private static class IndentItem implements Item {
        private final int indent;

        public IndentItem(int indent) {
            this.indent = indent;
        }

        public void write(Writer out) throws IOException {
            for (int i = 0; i < indent; i++) {
                out.append(" ");
            }
        }
    }
}

如果你不希望在一个单独的线程来写(也许在测试?),你可以有一个实现FileWriter它要求appendWriter在调用者线程。



 类似资料:
  • 问题内容: 我需要使用java nio将巨大的字符串写入(附加)到平面文件。编码为ISO-8859-1。 目前,我们正在编写如下图所示。有没有 更好的 方法可以做到这一点? 编辑:尝试了两个选项。以下是结果。 使用ByteBuffer和Channel:花费了4402 ms 使用缓冲的Writer:占用563毫秒 问题答案: 我认为,如果不对软件进行基准测试,您将无法得到严格的答案。在适当的条件下,

  • 问题内容: 使用Java在文件中间写入字节的最佳方法是什么? 问题答案: 在文件中间进行读写与在Java中使用一样简单。 尽管有它的名字,但它更像是和而不是像。它使您可以读取或查找文件,然后开始写您想停在的字节。 一旦发现此类,如果您对常规文件I / O有基本的了解,它将非常容易使用。 一个小例子:

  • 问题内容: 我想重写文件的内容。 到目前为止,我想到的是: 保存文件名 删除现有文件 用相同的名称创建一个新的空文件 将所需内容写入空文件 这是最好的方法吗?还是有更直接的方法,即不必删除和创建文件,而只需更改内容? 问题答案: 要使用FileOutputStream覆盖文件foo.log,请执行以下操作: 或使用FileWriter:

  • 问题内容: 这就是我逐行处理文件的方式。但是,在这种情况下,我想在每次迭代中向处理器发送 两 行文本。(我正在处理的文本文件实际上在两行上存储了一条记录,因此每次都向处理器发送一条记录。) 用Java做到这一点的最佳方法是什么? 问题答案: 为什么不读两行呢? 假设您可以依靠输入文件中包含完整的2行数据集。

  • 我有一个java程序,它向Web服务发送一系列GET请求,并将响应体存储为文本文件。 我已经实现了下面的示例代码(过滤了大部分代码以突出显示相关内容),它附加了文本文件,并在EOF处作为新行写入。然而,代码工作得很好,但随着文件的大小变大,性能会受到影响。 数据的总大小几乎为4GB,并在avg上附加大约500KB到1MB的数据。 这些文件每天都会创建,并移动到hdfs以供hadoop使用,并作为实

  • 问题内容: 目前,我正在使用类似: 我对这种方法不是很满意,因为它会创建大量数组(可以包含一本书)。 有没有更好的解决方案来迭代a的行? 问题答案: 您可以使用: 并使用方法: