È un blocco di lettura su un ReentrantReadWriteLock sufficiente per la lettura contemporanea di un RandomAccessFile
-
22-09-2019 - |
Domanda
sto scrivendo qualcosa per gestire le richieste simultanee di lettura / scrittura in un file di database.
ReentrantReadWriteLock si presenta come una buona partita. Se tutti i thread accedono un oggetto condiviso RandomAccessFile , ho bisogno di preoccuparsi per il puntatore del file con i lettori concorrenti? Considerate questo esempio:
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;
}
}
Nel metodo GetRecord (), è il seguente interleaving possibile con più lettori simultanei?
Thread 1 -> GetRecord (0)
Discussione 2 -> GetRecord (1)
Discussione 1 -> acquisisce condiviso blocco
Discussione 2 -> acquisisce condiviso blocco
Discussione 1 -> cerca di registrare 0
Discussione 2 -> cerca di registrare 1
Discussione 1 -> si legge record puntatore a file (1)
Discussione 2 -> si legge record puntatore a file (1)
Se ci sono infatti i potenziali problemi di concorrenza utilizzando ReentrantReadWriteLock e RandomAccessFile, quale sarebbe un'alternativa essere?
Soluzione
Sì, questo codice non è sincronizzato correttamente, proprio come si contorno. Un blocco di lettura-scrittura non è utile se il blocco di scrittura non è mai acquisita; è come se non v'è alcun blocco.
Utilizzare un blocco synchronized
tradizionale per rendere la ricerca e lettura appaiono atomica ad altri thread, o di creare un pool di istanze RandomAccessFile
che sono presi in prestito per l'uso esclusivo di un singolo thread e poi restituiti. (O semplicemente dedicare un canale per ogni thread, se non si dispone di troppi thread.)
Altri suggerimenti
Questo è un esempio di programma che bloccare il file e file di sbloccare.
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) { }
Si consiglia di considerare l'utilizzo di blocchi di file di sistema, invece di gestire il proprio blocco.
chiamata getChannel().lock()
sul RandomAccessFile per bloccare il file tramite la classe FileChannel
. Questo impedisce l'accesso in scrittura, anche dai processi al di fuori del vostro controllo.
Piuttosto operare sull'oggetto blocco singolo invece del metodo, ReentrantReadWriteLock può supportare fino a un massimo di 65535 blocchi di scrittura ricorsive e 65535 leggere serrature.
Assegnare una lettura e scrittura di blocco
private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();
Quindi lavorare su di loro ...
Inoltre: non sono catering per un'eccezione e fallimento per sbloccare successivamente alla chiusura. Chiamare la serratura come si entra il metodo (come un armadietto mutex) e poi fare il vostro lavoro in un blocco try / catch con lo sblocco nella sezione, infine, ad esempio:
public String[] allKeys() {
r.lock();
try { return m.keySet().toArray(); }
finally { r.unlock(); }
}
OK, 8,5 anni è un tempo lungo, ma spero che non è necro ...
Il mio problema era che avevamo bisogno di accedere a flussi di leggere e scrivere come atomica possibile. Una parte importante è che il nostro codice avrebbe dovuto funzionare su più macchine che accedono lo stesso file. Tuttavia, tutti gli esempi su Internet fermato a spiegare come bloccare un RandomAccessFile
e non andare più in profondità. Quindi il mio punto di partenza è stato di Sam risposta .
Ora, a distanza ha senso avere un certo ordine:
- bloccare il file
- Apri i flussi
- fare tutto ciò con i flussi
- chiudere i flussi
- rilasciare il blocco
Tuttavia, per consentire il rilascio del blocco in Java i flussi non devono essere chiuse! A causa di ciò l'intero meccanismo diventa un po 'strano (e sbagliato?).
Al fine di rendere il lavoro di auto-chiusura si deve ricordare che JVM chiude le entità in ordine inverso del try-segmento. Ciò significa che un flusso simile a questo:
- Apri i flussi
- bloccare il file
- fare tutto ciò con i flussi
- rilasciare il blocco
- chiudere i flussi
I test hanno dimostrato che questo non funziona. Pertanto, chiusura automatica a metà strada e fare il resto in buona ol' Java 1 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();
}
Si noti che i metodi che utilizzano questo devono ancora essere synchronized
. In caso contrario, le esecuzioni parallele possono gettare un OverlappingFileLockException
quando si chiama lock()
.
Si prega di condividere le esperienze nel caso si abbia qualsiasi ...