2012-03-01 14 views
6

Voglio scrivere alcune stringhe su un file. Quindi, ho usato la classe BufferedWriter. Poiché molti thread tendono a scrivere in quel file, voglio sapere se scrivere e writeLine i metodi sono atomici o meno.Scrittore è un metodo atomico?

Inoltre, voglio che il programma scriva l'output su più file e 100 righe per file (ad esempio file.txt0, file.txt1, ...). Per esempio

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); 
    } 
} 

Qualcuno può aiutarmi? Se non sono atomici, come posso renderli atomici ed evitare la collisione?

risposta

6

No, quelli non sono atomici.

Se si desidera più scritture sullo stesso file, utilizzare 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. Buono a sapersi. – Mudassir

+0

bel lavoro. Se voglio che scriva le uscite su più file, 100 linee per file, cosa posso fare? – orezvani

+0

@emab, dipende. Dovrai modificare la tua domanda con maggiori dettagli su cosa (esattamente) vuoi fare (: – Marcelo

3

È possibile utilizzare FileLock ma può essere costoso.

Personalmente, vorrei utilizzare un blocco di oggetti ordinario. per esempio.

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

Ma qui il buffereredWriter deve essere definitivo.Il mio buffereredWriter non è definitivo, perché apre un nuovo file ogni 100 righe di output. – orezvani

+0

In tal caso è necessario un oggetto che è 'final' che è possibile utilizzare. dovrebbe anche bloccare qualsiasi riapertura del file. –

5

Il codice sotto è codice sorgente da JDK6, è l'attuazione di scrittura in BufferedWriter, causa c'è un synchronized nel corpo della funzione, Credo il write() in BufferedWriter è sicuro thread. Btw, write(String) è implementato chiamando 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

Questo rende ogni scrittura atomica, ma il codice dell'OP ha ancora un problema di concorrenza quando si chiama 'bw.write (str); bw.writeLine();'. – assylias

+0

@assylias Questo non è l'unico problema: flushToBuffer finisce per scrivere sul file. Quindi, se un thread concorrente dovesse leggere il file, potrebbe leggere dati parziali. – user1706991

1

NGloom ragione, la seguente è una discarica filo di una parte di un programma che fa accesso concorrente a BufferredWriter.append() metodo (Oracle JDK 7 runtime) che non utilizza alcuna sincronizzazione sul corpo del metodo. È abbastanza chiaro che l'implementazione di BufferredWriter.append() utilizza un monitor sull'istanza dell'oggetto BufferredWriter, quindi è thread-safe. Tuttavia non riesco a trovare nulla sulla sicurezza dei thread on the related java doc e quindi l'API non fornisce tali garanzie, in quanto tali implementazioni potrebbero variare? Inoltre, il fatto che Writer.write() sia sicuro per i thread non impedisce ad un altro writer di includere un diverso oggetto OutputStream sullo stesso file per tentare di scrivere contemporaneamente ad esso, il che non è sicuro.

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

Sì, BufferedWriter è thread-safe. Vedi lo snippet qui sotto. Durante la scrittura del blocco sincronizzato contenuto garantisce la sicurezza del thread.

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

enter image description here