2012-03-01 16 views
6

Quiero escribir algunas cadenas en un archivo. Entonces, utilicé BufferedWriter clase. Como muchos hilos tienden a escribir en ese archivo, quiero saber si escribir y writeLine los métodos son atómicos o no.¿Es el escritor un método atómico?

Además, quiero que el programa escriba la salida en varios archivos y 100 líneas por archivo (digamos archivo.txt0, archivo.txt1, ...). Por ejemplo

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

¿Alguien me puede ayudar? Si no son atómicos, ¿cómo puedo hacerlos atómicos y evitar la colisión?

Respuesta

6

No, esos no son atómicos.

Si quiere escribir múltiples veces en el mismo archivo, use 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. Bueno saber. – Mudassir

+0

buen trabajo. Si quiero que escriba las salidas en varios archivos, 100 líneas por archivo, ¿qué puedo hacer? – orezvani

+0

@emab, eso depende. Deberá editar su pregunta con más detalles de lo que (exactamente) desea hacer (: – Marcelo

3

Puede usar FileLocks, pero puede ser costoso.

Personalmente, usaría un bloqueo de objeto ordinario. p.ej.

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

Pero aquí el bufferedWriter debe ser final. Mi bufferedWriter no es definitivo, ya que abre un nuevo archivo cada 100 líneas de salida. – orezvani

+0

En ese caso necesitas un objeto que sea 'final' el cual puedes usar. también debe cerrarse alrededor de cualquier reapertura del archivo. –

5

El código siguiente es el código fuente de jdk6, es la puesta en práctica de escritura en BufferedWriter, porque hay un synchronized en el cuerpo de la función, creo que el write() en BufferedWriter es seguro para subprocesos. Por cierto, write(String) se implementa llamando al 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

Eso hace que cada escritura sea atómica, pero el código del OP aún tiene un problema de concurrencia al llamar 'bw.write (str); bw.writeLine();'. – assylias

+0

@assylias Ese no es el único problema: el flushToBuffer termina escribiendo en el archivo. Entonces, si un hilo concurrente fuera a leer el archivo, podría leer datos parciales. – user1706991

1

NGloom es correcto, el siguiente es un volcado de hilo de una parte de un programa que hace el acceso simultáneo a BufferredWriter.append() método (Oracle JDK 7 de tiempo de ejecución), que utiliza sin sincronización en absoluto en el cuerpo del método. Está bastante claro que la implementación de BufferredWriter.append() usa un monitor en la instancia del objeto BufferredWriter, por lo que es seguro para subprocesos. Sin embargo, no puedo encontrar nada sobre seguridad de hilos on the related java doc y, por lo tanto, la API no ofrece ninguna garantía, ya que estas implementaciones pueden variar. Además, el hecho de que Writer.write() es seguro para hilos no impide que otro escritor que envuelve un objeto OutputStream diferente al mismo archivo intente escribir simultáneamente, lo que no es seguro.

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 is thread safe. Vea el fragmento a continuación. Al escribir el contenido, el bloque sincronizado garantiza la seguridad del hilo.

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

enter image description here

Cuestiones relacionadas