Frage

Ich bin etwas zu schreiben, die gleichzeitigen Lese- / Schreibanforderungen an eine Datenbank-Datei zu bearbeiten.

ReentrantReadWriteLock sieht aus wie ein gutes Spiel. Wenn alle Threads Zugriff auf ein gemeinsames Random Objekt , muss ich Sorge um die Dateizeiger bei gleichzeitiger Leser? Betrachten Sie folgendes Beispiel:

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

}

In der GetRecord () Methode ist die folgende Verschachtelung möglich mit mehreren gleichzeitigen Lesern?

  

Thread 1 -> GetRecord (0)
  Gewinde 2 -> GetRecord (1)
  Thema 1 -> erwirbt gemeinsame Sperre
  Thread 2 -> erwirbt gemeinsame Sperre
  Thema 1 -> sucht 0
aufnehmen   Thread 2 -> sucht 1 | aufzeichnen   Thema 1 -> liest Datensatz bei Dateizeiger (1)
  Fädeln 2 -> liest Datensatz bei Dateizeiger (1)

Wenn es tatsächlich mögliche Parallelitätsprobleme mit ReentrantReadWriteLock und Random, was wäre eine Alternative?

War es hilfreich?

Lösung

Ja, dieser Code nicht richtig synchronisiert, so wie Sie skizzieren. Eine Lese-Schreibsperre ist nicht sinnvoll, wenn die Schreibsperre nie erworben wird; es ist, als ob es keine Sperre ist.

Mit einem traditionellen synchronized Block der Such- und Lese erscheinen Atom zu anderen Threads, oder erstellen Sie einen Pool von RandomAccessFile Instanzen zu machen, die für die ausschließliche Verwendung von einem einzigen Thread entlehnt sind und dann zurückgegeben. (Oder einfach widmen einen Kanal zu jedem Thread, wenn Sie zu nicht viele Threads haben.)

Andere Tipps

Dies ist ein Beispielprogramm, das Sperrdatei und Unlock-Datei.

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

Sie können wollen Schlösser mit File System betrachten, anstatt Ihre eigene Verriegelung der Verwaltung.

Anruf getChannel().lock() auf Random die Datei über die FileChannel Klasse zu sperren. Dies verhindert, dass der Schreibzugriff, auch von Prozessen außerhalb Ihrer Kontrolle.

Vielmehr arbeitet auf dem einzelnen Sperrobjekt eher als das Verfahren, ReentrantReadWriteLock kann bis zu einem Maximum von 65.535 rekursiven Schreibsperren unterstützen und 65.535 Lesesperren.

Assign eine Lese- und Schreibsperre

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

Dann Arbeit auf sie ...

Sie auch: Sie sind Catering nicht für eine Ausnahme und Versagen zu entsperren im Anschluss an verriegeln. Rufen Sie das Schloss, wie Sie das Verfahren (wie ein Mutex Schließfach), dann tun Sie Ihre Arbeit in einem try / catch-Block mit dem Unlock in dem schließlich Abschnitt eingeben, zB:

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

OK, beträgt 8,5 Jahre sind eine lange Zeit, aber ich hoffe, es ist nicht necro ...

Mein Problem war, dass wir Zugang Ströme benötigen wie möglich zu lesen und zu schreiben als Atom. Ein wichtiger Teil war, dass unser Code sollte auf mehreren Rechnern laufen die gleiche Datei zugreifen. Allerdings sind alle Beispiele im Internet gestoppt zu erklären, wie ein RandomAccessFile zu sperren und nicht tiefer ging. Also mein Ausgangspunkt war Sams Antwort .

Nun, aus der Ferne macht es Sinn, eine gewisse Ordnung zu haben:

  • sperrt die Datei
  • Öffnen Sie die Ströme
  • tun, was mit den Strömen
  • schließen die Ströme
  • die Sperre

Um jedoch zu ermöglichen, das Schloss in Java Freigabe die Ströme müssen nicht geschlossen werden! Aufgrund der, dass der gesamte Mechanismus wird ein wenig seltsam (und falsch?).

Um die automatische Schließung der Arbeit zu machen muß bedenken, dass JVM die Entitäten in der umgekehrten Reihenfolge des Try-Segments geschlossen wird. Dies bedeutet, dass ein Fluss sieht wie folgt aus:

  • Öffnen Sie die Ströme
  • sperrt die Datei
  • tun, was mit den Strömen
  • die Sperre
  • schließen die Ströme

zeigten Tests, dass dies nicht funktioniert. Daher automatische Schließen auf halben Weg und macht den Rest in good ol‘Java 1 Art und Weise:

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

Beachten Sie, dass die Methoden, diese verwenden, müssen noch synchronized sein. Ansonsten sind die parallelen Ausführungen kann eine OverlappingFileLockException werfen, wenn lock() aufrufen.

Bitte teilen Erfahrungen haben, falls Sie eine ...

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top