Es un bloqueo de lectura en un ReentrantReadWriteLock suficiente para la lectura simultánea de un RandomAccessFile

StackOverflow https://stackoverflow.com/questions/1587218

Pregunta

Estoy escribiendo algo para manejar las peticiones de lectura / escritura simultáneas en un archivo de base de datos.

ReentrantReadWriteLock se parece a un partido bueno. Si todos los subprocesos tienen acceso a un RandomAccessFile objeto compartido , ¿necesito que preocuparse por el puntero de fichero con los lectores simultáneos? Considere este ejemplo:

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Database {

    private static final int RECORD_SIZE = 50;
    private static Database instance = null;

    private ReentrantReadWriteLock lock;
    private RandomAccessFile database;

    private Database() {
        lock = new ReentrantReadWriteLock();

        try {
            database = new RandomAccessFile("foo.db", "rwd");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    };

    public static synchronized Database getInstance() {
        if(instance == null) {
            instance = new Database();
        }
        return instance;
    }

    public byte[] getRecord(int n) {
        byte[] data = new byte[RECORD_SIZE];
        try {
            // Begin critical section
            lock.readLock().lock();
            database.seek(RECORD_SIZE*n);
            database.readFully(data);
            lock.readLock().unlock();
            // End critical section
        } catch (IOException e) {
            e.printStackTrace();
        }
        return data;
    }

}

En el método getRecord (), es el siguiente entrelazado posible con múltiples lectores concurrentes?

  

Tema 1 -> getRecord (0)
  Enhebrar 2 -> getRecord (1)
  Rosca 1 -> adquiere bloqueo compartido
  Rosca 2 -> adquiere bloqueo compartido
  Rosca 1 -> 0 pretende registrar
  Rosca 2 -> busca para grabar 1 |   Thread 1 -> lee en el registro de puntero de archivo (1)
  Rosca 2 -> lee en el registro de puntero de archivo (1)

Si hay de hecho posibles problemas de concurrencia utilizando ReentrantReadWriteLock y RandomAccessFile, lo que lo haría un ser alternativos?

¿Fue útil?

Solución

Sí, este código no está sincronizado correctamente, tal y como lo contorno. Un bloqueo de lectura-escritura no es útil si el bloqueo de escritura no se adquiere; es como si no hay bloqueo.

Use un bloque de synchronized tradicional para hacer la búsqueda y lectura aparece atómica a otros hilos, o crear un grupo de instancias RandomAccessFile tomados en préstamo para el uso exclusivo de un solo hilo y luego regresó. (O simplemente dedicar un canal para cada hilo, si usted no tiene demasiadas hebras.)

Otros consejos

Este es un programa de ejemplo que el archivo de bloqueo y desbloqueo de archivos.

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


    lock.release(); // Close the file 

    channel.close();
} 

catch (Exception e) { } 

Es posible que desee considerar el uso de bloqueos de archivos del sistema en lugar de gestionar su propio bloqueo.

getChannel().lock() en su RandomAccessFile de llamadas para bloquear el archivo a través de la clase FileChannel. Esto impide el acceso de escritura, incluso de procesos fuera de su control.

Más bien operar en el objeto de bloqueo único en lugar del método, ReentrantReadWriteLock puede soportar hasta un máximo de 65535 bloqueos de escritura recursivas y 65.535 leer cerraduras.

Asignar una lectura y escritura de bloqueo

private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();

A continuación, el trabajo en ellos ...

También: usted no está de vacaciones para una excepción y el fracaso para desbloquear posterior al bloqueo. Llamar al bloqueo al entrar en el método (como un armario mutex) y luego hacer su trabajo en un bloque try / catch con el desbloqueo de la sección, por último, por ejemplo:

public String[] allKeys() {
  r.lock();
  try { return m.keySet().toArray(); }
  finally { r.unlock(); }
}

OK, 8,5 años es mucho tiempo, pero espero que no sea necro ...

Mi problema era que teníamos que las corrientes de acceso a leer y escribir como atómica como sea posible. Una parte importante fue que nuestro código se supone que debe ejecutarse en múltiples máquinas que accedan al mismo archivo. Sin embargo, todos los ejemplos en Internet se detuvo en explicar cómo bloquear un RandomAccessFile y no ir más profundo. Así que mi punto de partida fue de Sam respuesta .

Ahora, desde la distancia que tiene sentido tener un cierto orden:

  • bloquear el archivo
  • abrir las corrientes
  • hacer lo que con las corrientes
  • cerca las corrientes
  • liberar el bloqueo

Sin embargo, para permitir liberar el bloqueo en Java las corrientes no debe ser cerrado! Debido a que todo el mecanismo se vuelve un poco raro (y el mal?).

Con el fin de hacer un trabajo auto-cierre debe recordar que JVM cierra las entidades en el orden inverso de la try-segmento. Esto significa que un flujo es similar al siguiente:

  • abrir las corrientes
  • bloquear el archivo
  • hacer lo que con las corrientes
  • liberar el bloqueo
  • cerca las corrientes

Las pruebas demostraron que esto no funciona. Por lo tanto, el cierre automático que a mitad de camino y hacer el resto en buen ol' Java 1 de la moda:

try (RandomAccessFile raf = new RandomAccessFile(filename, "rwd");
    FileChannel channel = raf.getChannel()) {
  FileLock lock = channel.lock();
  FileInputStream in = new FileInputStream(raf.getFD());
  FileOutputStream out = new FileOutputStream(raf.getFD());

  // do all reading
  ...

  // that moved the pointer in the channel to somewhere in the file,
  // therefore reposition it to the beginning:
  channel.position(0);
  // as the new content might be shorter it's a requirement to do this, too:
  channel.truncate(0);

  // do all writing
  ...

  out.flush();
  lock.release();
  in.close();
  out.close();
}

Tenga en cuenta que los métodos que utilizan este debe seguir siendo synchronized. De lo contrario, las ejecuciones paralelas pueden lanzar una OverlappingFileLockException al llamar lock().

Por favor, compartir experiencias en caso de tener alguna ...

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top