在Java中,在并行线程中写入文件的最佳方式是什么?

在Java中,在并行线程中写入文件的最佳方式是什么?,java,multithreading,file,Java,Multithreading,File,我有一个程序,可以执行大量计算,并经常将它们报告到文件中。我知道频繁的写操作会大大降低程序的速度,所以为了避免这种情况,我希望有第二个线程专门用于写操作 现在我正在用我写的这门课做这个(不耐烦的人可以跳到问题的末尾): 虽然这很有效,但我想知道: 有更好的方法来实现这一点吗?使用LinkedBlockingQueue是一个非常好的主意。不确定我是否喜欢代码的某些样式。。。但这一原则似乎是合理的 我可能会向LinkedBlockingQueue添加一个容量,相当于总内存的某个百分比。。比如说一万件

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

现在我正在用我写的这门课做这个(不耐烦的人可以跳到问题的末尾):

虽然这很有效,但我想知道:
有更好的方法来实现这一点吗?

使用LinkedBlockingQueue是一个非常好的主意。不确定我是否喜欢代码的某些样式。。。但这一原则似乎是合理的


我可能会向LinkedBlockingQueue添加一个容量,相当于总内存的某个百分比。。比如说一万件。。这样,如果您的编写速度太慢,您的工作线程将无法继续添加更多工作,直到堆被破坏。

您的基本方法看起来不错。我将按照以下方式构建代码:

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(" ");
            }
        }
    }
}
导入java.io.BufferedWriter;
导入java.io.File;
导入java.io.IOException;
导入java.io.Writer;
导入java.util.concurrent.BlockingQueue;
导入java.util.concurrent.LinkedBlockingQueue;
导入java.util.concurrent.TimeUnit;
公共接口文件编写器{
FileWriter追加(CharSequence seq);
文件写入器缩进(int缩进);
无效关闭();
}
类AsyncFileWriter实现FileWriter,可运行{
私人最终文件;
私人最终撰稿人;
private final BlockingQueue=新建LinkedBlockingQueue();
private volatile boolean start=false;
private volatile boolean stopped=false;
公共AsyncFileWriter(文件)引发IOException{
this.file=文件;
this.out=new BufferedWriter(new java.io.FileWriter(file));
}
公共FileWriter追加(字符序列){
如果(!已启动){
抛出新的IllegalStateException(“在append()之前需要open()调用”);
}
试一试{
queue.put(新的CharSeqItem(seq));
}捕获(InterruptedException被忽略){
}
归还这个;
}
公共文件编写器缩进(int缩进){
如果(!已启动){
抛出新的IllegalStateException(“在append()之前需要open()调用”);
}
试一试{
queue.put(新缩进项(缩进));
}捕获(InterruptedException被忽略){
}
归还这个;
}
公开作废{
this.started=true;
新线程(this.start();
}
公开募捐{
当(!停止){
试一试{
Item=queue.poll(100,时间单位为微秒);
如果(项!=null){
试一试{
项目.填写(出);
}捕获(IOException logme){
}
}
}捕捉(中断异常e){
}
}
试一试{
out.close();
}捕获(IOException忽略){
}
}
公众假期结束(){
this.stopped=true;
}
专用静态接口项{
void write(Writer out)抛出IOException;
}
私有静态类CharSeqItem实现该项{
私有最终字符序列;
公共字符序列项(字符序列){
这个序列=序列;
}
public void write(Writer out)引发IOException{
out.append(序列);
}
}
私有静态类IndentItem实现项{
私人最终整数缩进;
公共缩进项(整数缩进){
this.indent=缩进;
}
public void write(Writer out)引发IOException{
对于(int i=0;i

如果您不想在单独的线程中写入(可能在测试中?),可以使用
FileWriter
的实现,在调用线程中的
Writer
上调用
append

与单个使用者线程交换数据的一个好方法是使用交换器

您可以使用StringBuilder或ByteBuffer作为缓冲区与后台线程交换。产生的延迟大约为1微秒,不涉及创建任何对象,使用BlockingQueue的延迟更低

我认为这个例子值得在这里重复

class FillAndEmpty {
   Exchanger<DataBuffer> exchanger = new Exchanger<DataBuffer>();
   DataBuffer initialEmptyBuffer = ... a made-up type
   DataBuffer initialFullBuffer = ...

   class FillingLoop implements Runnable {
     public void run() {
       DataBuffer currentBuffer = initialEmptyBuffer;
       try {
         while (currentBuffer != null) {
           addToBuffer(currentBuffer);
           if (currentBuffer.isFull())
             currentBuffer = exchanger.exchange(currentBuffer);
         }
       } catch (InterruptedException ex) { ... handle ... }
     }
   }

   class EmptyingLoop implements Runnable {
     public void run() {
       DataBuffer currentBuffer = initialFullBuffer;
       try {
         while (currentBuffer != null) {
           takeFromBuffer(currentBuffer);
           if (currentBuffer.isEmpty())
             currentBuffer = exchanger.exchange(currentBuffer);
         }
       } catch (InterruptedException ex) { ... handle ...}
     }
   }

   void start() {
     new Thread(new FillingLoop()).start();
     new Thread(new EmptyingLoop()).start();
   }
 }
类FillAndEmpty{
交换器=新交换器();
DataBuffer initialEmptyBuffer=…一个组合类型
DataBuffer initialFullBuffer=。。。
类FillingLoop实现Runnable{
公开募捐{
DataBuffer currentBuffer=initialEmptyBuffer;
试一试{
while(currentBuffer!=null){
addToBuffer(当前缓冲区);
if(currentBuffer.isFull())
currentBuffer=exchanger.exchange(currentBuffer);
}
}catch(InterruptedException ex){…handle…}
}
}
类EmptyingLoop实现可运行{
公开募捐{
DataBuffer currentBuffer=initialFullBuffer;
试一试{
while(currentBuffer!=null){
takeFromBuffer(currentBuffer);
if(currentBuffer.isEmpty())
currentBuffer=exchanger.exchange(currentBuffer);
}
}catch(InterruptedException ex){…handle…}
}
}
void start(){
新线程(新FillingLoop()).start();
新线程(新清空循环()).start();
}
}
我知道频繁的写操作 会使程序慢很多


如果使用缓冲,可能没有您想象的那么多

谢谢,将特定于项目的任务委托给项目更符合OOP,而不是我的做法。另外,使用
this.stopped
结束读取与使用有毒元素相比是否有一个特别的优点?此外,缩进操作的作用稍有不同:我的缩进设置所有futu的缩进
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(" ");
            }
        }
    }
}
class FillAndEmpty {
   Exchanger<DataBuffer> exchanger = new Exchanger<DataBuffer>();
   DataBuffer initialEmptyBuffer = ... a made-up type
   DataBuffer initialFullBuffer = ...

   class FillingLoop implements Runnable {
     public void run() {
       DataBuffer currentBuffer = initialEmptyBuffer;
       try {
         while (currentBuffer != null) {
           addToBuffer(currentBuffer);
           if (currentBuffer.isFull())
             currentBuffer = exchanger.exchange(currentBuffer);
         }
       } catch (InterruptedException ex) { ... handle ... }
     }
   }

   class EmptyingLoop implements Runnable {
     public void run() {
       DataBuffer currentBuffer = initialFullBuffer;
       try {
         while (currentBuffer != null) {
           takeFromBuffer(currentBuffer);
           if (currentBuffer.isEmpty())
             currentBuffer = exchanger.exchange(currentBuffer);
         }
       } catch (InterruptedException ex) { ... handle ...}
     }
   }

   void start() {
     new Thread(new FillingLoop()).start();
     new Thread(new EmptyingLoop()).start();
   }
 }