いくつかのスレッドを使用して単一のファイルにデータを書き込むときに、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();
}
}
}
}
ライターではなくOutputStreamを使用して問題を解決します。コードは次のとおりです。
bw = new BufferedOutputStream(
new FileOutputStream(new File("/Users/liaoliuqing/Downloads/1.txt"),true),165537);
FileWriter
は、実際には独自の固定サイズの1024バイトバッファを使用します。一方、BufferedWriter
は、ユーザーが他の任意のサイズに構成できる8192バイトのバッファーサイズ(デフォルト)を使用していることを示しています。
さらに水を濁らせるために、OutputStreamWriter
のJava 6実装は実際にはStreamEncoder
に委任します。これは、デフォルトサイズ8192バイトの独自のバッファーを使用します。 。また、StreamEncoder
バッファーはユーザーが構成できますが、囲んでいるOutputStreamWriterから直接アクセスする方法はありません。
コードに問題はありますか?
いくつか。主に:潜在的なIOおよび同時実行エラー。ファイルバッファサイズはそれほど懸念されない可能性があります(そして効果的に対処できないもの)。
すでに開いているファイルを開こうとしています。すべてのスレッドが同じファイルに書き込もうとしています(1.txt
)。それが問題になるかもしれません。 FileWriterのドキュメントによると:
特に一部のプラットフォームでは、一度に1つのFileWriter(または他のファイル書き込みオブジェクト)のみが書き込み用にファイルを開くことができます。このような状況では、関連するファイルがすでに開いていると、このクラスのコンストラクターは失敗します。
線がカットされて混合される可能性があります。ある時点でそれぞれのバッファが同じ出力にフラッシュする複数のスレッドがある場合、出力が破損していることを確認するために、奇妙な競合状態やスレッドを途中で停止したり、書き込み操作を行ったりする必要はありません。
私が解決したように(スレッドが同じ出力を共有する必要がある場合)、同期アクセスを備えた共有オブジェクトを使用して、実際の書き込みを処理できます。私の例ではSafeAppender
を実装しましたが、おそらくもっと良い選択肢があります。
いいえフラッシュおよびクローズバッファはデータ(の末尾)を意味します(雨の中の涙のように)失われます。通常、finallyブロックはそれを処理するのに適しています。
また、他のユーザーが述べているように、BufferedWriter
バッファサイズはFileOutputStream
のバッファサイズに影響しません(したがってFileWriter
)。そしてそれはJava.io
およびJava.nio
APIは、それを台無しにする方法を提供しません。 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);
}
}
}
表示されているのは、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)));