如何通过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(); } } } } 

FileWriter实际上使用自己的固定大小的1024字节缓冲区。 另一方面, BufferedWriter显示它使用和8192字节缓冲区大小(默认),可以由用户配置为任何其他所需大小。

为了进一步StreamEncoderOutputStreamWriter的Java 6实现实际上委托给StreamEncoder ,它使用自己的缓冲区,默认大小为8192字节。 StreamEncoder缓冲区是用户可配置的,尽管无法通过封闭的OutputStreamWriter直接访问它。

我的代码有问题吗?

一些。 主要是:潜在的IO和并发错误。 文件缓冲区大小可能是一个较小的问题(并且您无法有效处理)。

  • 试图打开已打开的文件 。 你所有的线程都试图写入同一个文件( 1.txt )。 这可能是一个问题。 FileWriter文档说:

    特别是某些平台允许一次只打开一个FileWriter(或其他文件写入对象)来写文件。 在这种情况下,如果涉及的文件已经打开,则此类中的构造函数将失败。

  • 线条可能被切割和混合 。 如果你有几个线程,它们各自的缓冲区在某一点刷新到相同的输出,你可能甚至不需要奇怪的竞争条件或线程中间停止的线程或写操作看到你的输出被破坏。

    在我解决方案时(如果您的线程必须共享相同的输出),您可以使用具有同步访问权限的共享对象来处理实际写入。 我在我的例子中实现了SafeAppender ,但可能还有更好的选择。

  • 没有冲洗关闭缓冲区将意味着(尾部)您的数据将丢失(如雨中的泪水)。 最后一块通常很好处理。

  • 此外,如其他用户所述, BufferedWriter 缓冲区大小不会影响FileOutputStream (以及FileWriter )中的缓冲区大小。 它看起来java.iojava.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); } } } 

我通过使用OutputStream解决问题,而不是编写器,这里是代码:

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

你看到的不是缓冲区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)));