当我使用一些线程将数据写入单个文件时,我遇到了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();
}
}
}
}
答案 0 :(得分:2)
FileWriter
实际上使用自己的固定大小的1024字节缓冲区。另一方面,BufferedWriter
显示它使用和8192字节缓冲区大小(默认),用户可以将其配置为任何其他所需大小。
为了进一步混乱,OutputStreamWriter
的Java 6实现实际上委托给StreamEncoder
,它使用自己的缓冲区,默认大小为8192字节。 StreamEncoder
缓冲区是用户可配置的,尽管无法通过封闭的OutputStreamWriter直接访问它。
答案 1 :(得分:1)
我的代码有问题吗?
一些。主要是:潜在的IO和并发错误。文件缓冲区大小可能是一个较小的问题(而且你无法有效处理)。
尝试打开已打开的文件。您的所有线程都试图写入同一个文件(1.txt
)。这可能是一个问题。 FileWriter documentation says:
某些平台特别允许一次只打开一个FileWriter(或其他文件写入对象)来写文件。在这种情况下,如果涉及的文件已经打开,则此类中的构造函数将失败。
线条可能被剪切和混合。如果你有几个线程,它们各自的缓冲区在某一点刷新到同一个输出,你可能甚至不需要奇怪的竞争条件或线程中间停止的线程或写操作看到你的输出被破坏。
作为我的解决方案(如果您的线程必须共享相同的输出),您可以使用具有同步访问权限的共享对象来处理实际写入。我在我的示例中实现了SafeAppender
,但可能还有更好的替代方案。
没有刷新和关闭缓冲区将意味着(数据的尾部)您的数据将会丢失(如雨中的泪水)。最后一块通常很好处理。
此外,正如其他用户所述,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);
}
}
}
答案 2 :(得分:1)
我通过使用OutputStream解决问题,而不是编写器,这是代码:
bw = new BufferedOutputStream(
new FileOutputStream(new File("/Users/liaoliuqing/Downloads/1.txt"),true),165537);
答案 3 :(得分:0)
您所看到的不是缓冲区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)));