2012-03-01 58 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

相關問題