web-dev-qa-db-ja.com

FileWriterを介してBufferedWriterのバッファサイズを設定する方法

いくつかのスレッドを使用して単一のファイルにデータを書き込むときに、BufferedWriterで問題が発生しました。

BufferedWriterのバッファサイズを設定しましたが、設定した数に関係なく、バッファが8192(デフォルトのバッファサイズ)の場合、設定したサイズ(ここでは16384)ではなくデータをディスクにフラッシュします。 。コードに問題はありますか?

これが私がBufferedWriterを構築する方法です:

new BufferedWriter(new FileWriter(fileName, true), 16384);

これは完全なコードです:

import Java.io.BufferedWriter;
import Java.io.File;
import Java.io.FileWriter;
import Java.io.IOException;

public class Test1 {
    public static void main(String[] args) throws IOException {
        for(int i =0;i<10;i++){
            MyThread r = new MyThread();
            Thread t = new Thread(r);
            t.start();
        }
    }
}

class MyThread implements Runnable {
    public void run() {
        String s = "{addffffffkkkljlkj2015dd}\n";
        BufferedWriter bw = null;
        try {
            bw = new BufferedWriter(new FileWriter(
                    "/Users/liaoliuqing/Downloads/1.txt", true),16384);
        } catch (IOException e) {
            e.printStackTrace();
        }
        for(int i =0 ; i<1000; i++){
            try {
                bw.write(String.format("%03d", i)+s);
                //bw.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
8
jinhong_lu

ライターではなくOutputStreamを使用して問題を解決します。コードは次のとおりです。

bw = new BufferedOutputStream(
                new FileOutputStream(new File("/Users/liaoliuqing/Downloads/1.txt"),true),165537);
1
jinhong_lu

FileWriterは、実際には独自の固定サイズの1024バイトバッファを使用します。一方、BufferedWriterは、ユーザーが他の任意のサイズに構成できる8192バイトのバッファーサイズ(デフォルト)を使用していることを示しています。

さらに水を濁らせるために、OutputStreamWriterのJava 6実装は実際にはStreamEncoderに委任します。これは、デフォルトサイズ8192バイトの独自のバッファーを使用します。 。また、StreamEncoderバッファーはユーザーが構成できますが、囲んでいるOutputStreamWriterから直接アクセスする方法はありません。

3

コードに問題はありますか?

いくつか。主に:潜在的なIOおよび同時実行エラー。ファイルバッファサイズはそれほど懸念されない可能性があります(そして効果的に対処できないもの)。

  • すでに開いているファイルを開こうとしています。すべてのスレッドが同じファイルに書き込もうとしています(1.txt)。それが問題になるかもしれません。 FileWriterのドキュメントによると:

    特に一部のプラットフォームでは、一度に1つのFileWriter(または他のファイル書き込みオブジェクト)のみが書き込み用にファイルを開くことができます。このような状況では、関連するファイルがすでに開いていると、このクラスのコンストラクターは失敗します。

  • 線がカットされて混合される可能性があります。ある時点でそれぞれのバッファが同じ出力にフラッシュする複数のスレッドがある場合、出力が破損していることを確認するために、奇妙な競合状態やスレッドを途中で停止したり、書き込み操作を行ったりする必要はありません。

    私が解決したように(スレッドが同じ出力を共有する必要がある場合)、同期アクセスを備えた共有オブジェクトを使用して、実際の書き込みを処理できます。私の例ではSafeAppenderを実装しましたが、おそらくもっと良い選択肢があります。

  • いいえフラッシュおよびクローズバッファはデータ(の末尾)を意味します(雨の中の涙のように)失われます。通常、finallyブロックはそれを処理するのに適しています。

  • また、他のユーザーが述べているように、BufferedWriterバッファサイズFileOutputStreamのバッファサイズに影響しません(したがってFileWriter)。そしてそれはJava.ioおよびJava.nio AP​​Iは、それを台無しにする方法を提供しません。 Javaライブラリソースを見ると、BufferedWriterバッファサイズは、デリゲート出力に実際に書き込む前に格納する文字の量を意味していることに気付くかもしれません。デフォルトサイズ(8192)ほとんどの場合に最適であり、それを増やすと、メリットよりも多くの問題(より多くのデータが失われる可能性がある)を意味する場合があります。

これが私のコードです。

// http://stackoverflow.com/questions/32451526/how-to-set-the-buffer-size-on-a-bufferedwriter-over-a-filewriter
public class TestWriter {

public static class SafeAppender {
    private BufferedWriter bw;
    private int users = 0;
    public SafeAppender(File f) throws IOException {
        bw = new BufferedWriter(new FileWriter(f));
    }

    public synchronized void append(String s) throws IOException {
        bw.write(s);
    }
    public synchronized void incrUsers() { 
        users ++; 
    }
    public synchronized void decrUsers() {
        if (--users <= 0) {
            try {
                bw.flush();
                System.err.println("INFO-appender-flush()");
            } catch (Throwable whatever) { /* log-if-you-care*/}
        }
    }
    // Might be called by GC, or not
    @Override protected void finalize() throws Throwable {
        try {
            bw.close();
            System.err.println("INFO-appender-close()");
        } catch (Throwable whatever) { /* log-if-you-care */}
        super.finalize();
    }
}

private static class MyRunnable implements Runnable {
    final static String S = "{addffffffkkkljlkj2015dd}";
    SafeAppender appender;
    String threadId;
    public MyRunnable (SafeAppender a, String tid) {
        appender = a; threadId = tid;
    }

    public void run() {
        appender.incrUsers();
        try {
            for(int i =0 ; i<1000; i++){
                // NOTE: Not a good idea to printStackTrace if each line fails. Let thread fail
                String line = String.format("%s-%03d-%s\n", threadId, i, S);
                appender.append(line);
            }
        } catch (IOException e) {
            System.err.printf("ERROR-%s-%s\n", threadId, e.toString());
        } finally {
            appender.decrUsers();
        }
    }
}

public static void main(String[] args) {
    try {
        File f = File.createTempFile("TestWriter", ".txt");
        System.err.printf("INFO-main-Writing into %s\n", f.getCanonicalPath());
        SafeAppender appender = new SafeAppender (f);
        for(int i =0;i<10;i++){
            MyRunnable r = new MyRunnable(appender, ""+i);
            Thread t = new Thread(r);
            t.start();
        }
    } catch (Throwable e) {
        e.printStackTrace(System.err);
    }
}

}
3
Javier

表示されているのは、BufferedWriterバッファーのサイズではなく、FileWriterによって内部的に使用されるバッファーのサイズです。 Javaドキュメント( http://docs.Oracle.com/javase/7/docs/api/Java/io/FileWriter.html )からの引用

このクラスのコンストラクターは、デフォルトの文字エンコードとデフォルトのバイトバッファーサイズが受け入れ可能であることを前提としています。これらの値を自分で指定するには、FileOutputStreamでOutputStreamWriterを作成します。

したがって、データが実際にディスクに書き込まれるタイミングをきめ細かく制御したい場合は、BufferedWriterを次のようにインスタンス化する必要があります。

bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File('my_file.txt),true)));
0
Marco Sandrini