2012-03-01 28 views
6

我想写一些字符串到一个文件。所以,我用BufferedWriter类。由于许多线程倾向于写入该文件,因此我想知道写入writeLine方法是否为原子。另外,我希望程序将输出写入多个文件,每个文件(例如file.txt0,file.txt1,...)有100行。例如作家是原子方法吗?

public class Main { 
    static ExecutorService exec = Executors.newFixedThreadPool(5); 
    BufferedWriter bw; 
    public class myWriter implements Runnable { 
     String str; 
     myWriter (String str) { 
      this.str = str; 
     } 
     public void run() { 
      bw.write(str); 
      bw.writeLine(); 
     } 
    } 
    public static void main(String[] args) { 
     bw = new BufferedWriter(new FileWriter("train.txt")); 
     for (String arg: args) 
      exec.execute(new myWriter(arg)); 
     exec.awaitTermination(100000, TimeUnit.MILLISECONDS); 
    } 
} 

有人可以帮我吗?如果它们不是原子的,我怎样才能使它们原子化并避免碰撞?

回答

6

不,这些都不是原子的。

如果你想多重写入同一个文件,使用FileLocks

try { 
    // Get a file channel for the file 
    File file = new File("filename"); 
    FileChannel channel = new RandomAccessFile(file, "rw").getChannel(); 

    // Use the file channel to create a lock on the file. 
    // This method blocks until it can retrieve the lock. 
    FileLock lock = channel.lock(); 

    // Try acquiring the lock without blocking. This method returns 
    // null or throws an exception if the file is already locked. 
    try { 
     lock = channel.tryLock(); 
    } catch (OverlappingFileLockException e) { 
     // File is already locked in this thread or virtual machine 
    } 

    // Release the lock 
    lock.release(); 

    // Close the file 
    channel.close(); 
} catch (Exception e) { 
} 
+2

+1。很高兴知道。 – Mudassir 2012-03-01 07:54:24

+0

不错的工作。如果我希望它将输出写入多个文件,每个文件100行,我该怎么办? – orezvani 2012-03-01 08:12:04

+0

@emab,这取决于。你必须编辑你的问题,更详细地说明你想要做什么(:) – Marcelo 2012-03-01 08:16:05

3

您可以使用FileLocks,但可能会很贵。

就我个人而言,我会使用普通的对象锁。例如

synchronized(bufferedWriter) { 
    bufferedWriter.write stuff 
    bufferedWriter.write more stuff 
} 
+0

但是这里的bufferedWriter必须是最终的。我的bufferedWriter不是最终的,因为它每隔100行输出就会打开一个新文件。 – orezvani 2012-03-01 08:57:36

+0

在这种情况下,你需要一个可以使用的'final'对象。 – 2012-03-01 09:03:12

5

下面的代码是从JDK6源代码, 这是写在BufferedWriter的实施,导致有在函数体内​​,我觉得在BufferedWriterwrite()是线程安全的。顺便说一下,write(String)通过调用write(String,int,int)实现。

public void write(String s, int off, int len) throws IOException { 

    synchronized (lock) { 

     ensureOpen(); 

     int b = off, t = off + len; 

     while (b < t) { 

      int d = min(nChars - nextChar, t - b); 
      s.getChars(b, b + d, cb, nextChar); 
      b += d; 
      nextChar += d; 

      if (nextChar >= nChars) 
       flushBuffer(); 
      } 
     } 
    } 
} 
+0

这使得每个写操作都是原子的,但是当调用'bw.write(str); bw.writeLine();'时,OP的代码仍然存在并发问题。 – assylias 2013-03-27 17:49:29

+0

@assylias这不是唯一的问题 - flushToBuffer最终写入文件。所以如果一个并发线程读取文件,它可以读取部分数据。 – user1706991 2017-03-26 19:19:06

1

NGloom是正确的,下面是一个程序,它以BufferredWriter.append()方法(甲骨文JDK 7运行时),其不使用同步化任何对方法身体的并发访问的一部分的一个线程转储。很明显,BufferredWriter.append()的实现在BufferredWriter对象的实例上使用监视器,所以它是线程安全的。然而,我找不到线程安全on the related java doc的任何内容,因此API不作任何保证,因为这样的实现可能会有所不同?此外,Writer.write()是线程安全的事实并不妨碍另一个写入器将不同的OutputStream对象包装到同一个文件,以尝试对其同时写入,这是不安全的。

ForkJoinPool-1-worker-3" daemon prio=10 tid=0x00007f358c002800 nid=0x3e66 waiting for monitor entry [0x00007f360bdfb000] 
    java.lang.Thread.State: BLOCKED (on object monitor) 
    at java.io.BufferedWriter.write(BufferedWriter.java:220) 
    - waiting to lock <0x0000000760da06d8> (a java.io.OutputStreamWriter) 
    at java.io.Writer.write(Writer.java:157) 
    at java.io.Writer.append(Writer.java:227) 
    at ... 


ForkJoinPool-1-worker-2" daemon prio=10 tid=0x00007f358c001000 nid=0x3e65 waiting for monitor entry [0x00007f360befc000] 
    java.lang.Thread.State: BLOCKED (on object monitor) 
    at java.io.BufferedWriter.write(BufferedWriter.java:220) 
    - waiting to lock <0x0000000760da06d8> (a java.io.OutputStreamWriter) 
    at java.io.Writer.write(Writer.java:157) 
    at java.io.Writer.append(Writer.java:227) 
    at ... 
1

是的,BufferedWriter是线程安全的。请参阅下面的代码片段。在编写内容同步块时确保线程安全。

public void write(int c) throws IOException { 
    synchronized (lock) { 
     ensureOpen(); 
     if (nextChar >= nChars) 
      flushBuffer(); 
     cb[nextChar++] = (char) c; 
    } 
} 

enter image description here

相关问题