2012-03-01 7 views
6

いくつかの文字列をファイルに書きたいと思います。だから、私はBufferedWriterクラスを使用しました。多くのスレッドがそのファイルに書き込む傾向があるので、私はの書き込みのwriteLineのメソッドがアトミックかどうかを知りたいと思っています。作者はアトミックな方法ですか?

また、出力を複数のファイルに書き、ファイルごとに100行(file.txt0、file.txt1など)を書きたいとします。たとえば

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

+0

素敵な仕事。ファイルごとに100行の複数のファイルに出力を書きたい場合、どうすればよいですか? – orezvani

+0

@emab、それは依存します。 (正確に)何をしたいのかという詳細をつけて質問を編集する必要があります: – Marcelo

3

FileLocksは使用できますが、高価な場合があります。

個人的には、普通のオブジェクトロックを使用します。例えば

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

しかし、ここではbufferedWriterはfinalでなければなりません。私のbufferedWriterは、100行ごとに新しいファイルを開くので最終的ではありません – orezvani

+0

この場合、使用できる 'final'オブジェクトが必要です。ファイルの再オープンをロックする必要があります。 –

5

次のコードは、私がBufferedWriterwrite()は、スレッドセーフであると考え、関数本体内​​をあります原因、それはBufferedWriterでの書き込みの実装だ、 JDK6からソースコードです。 Btw、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

これはそれぞれのアトミックなアトミックを作成しますが、OPのコードは 'bw.write(str); bw.writeLine();'を呼び出すときに並行性の問題があります。 – assylias

+0

@assyliasこれは唯一の問題ではありません.flushToBufferはファイルへの書き込みを終了します。したがって、並行スレッドがファイルを読み取る場合、部分的なデータを読み取ることができます。 – user1706991

1

NGloomが正しいと、次は、メソッド本体に一切のsyncronizationを使用しないBufferredWriter.append()方法(Oracleの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

関連する問題