После некоторого серьезного googleing я узнал, что RandomAccessFile-класс не ориентирован на многопотоковое исполнение. Теперь я мог использовать один семафор для блокировки всех чтений и записей, но я не думаю, что это работает очень хорошо. В теории должно быть возможно сделать несколько чтений и одну запись за один раз. Как я могу сделать это в Java? Действительно ли это возможно вообще?
Спасибо!
Частичная блокировка файла - сложная задача, которую многие операционные системы избегают. Однако, если вы настаиваете на этом, один из способов - создать собственный объект механизма блокировки, который записывает, какие части файла заблокированы. По сути, перед чтением или записью объект должен запросить блокировку для определенного диапазона байтов файла. Блокировки считаются конфликтующими, если они вообще перекрываются в байтовом диапазоне. Блокировки чтения и записи обрабатываются по-разному: чтение может безопасно перекрываться с любым количеством блокировок чтения, но блокировка записи не должна перекрываться ни с какими другими блокировками, чтением или записью. Есть много вопросов о том, следует ли ждать или прервать выполнение, если вы не можете получить блокировку, и следует ли блокировать чтение во время ожидания записи, но только вы можете ответить на них о своем приложении.
Учитывая сложность этого, может быть лучше заблокировать весь файл. Проверьте, получаете ли вы адекватную производительность - и не забывайте, что вы можете разрешить несколько операций чтения одновременно, если нет записи.
Рассмотрим этот подход - он позволяет неограниченному количеству читателей, и когда писатель хочет написать, он ждет, пока текущие читатели закончат свою запись.
class readWriteSemaphore() {
private Object lock;
List<Thread> readers;
Thread writer;
readWriteSemaphore() {
readers = new LinkedList<Thread>(); // Linked list is inefficient for many threads, FYI
writer = null;
}
/**
* Returns true if and only if you have acquired a read
* maybe use while(!rws.acquireRead(Thread.currentThread())) Thread.sleep(50); // or something
*/
boolean acquireRead(Thread t) {
synchronized(lock) {
if(writer == null) {
readers.add(t);
return true;
}
return false; // yes this could go outside the synch block... oh well
}
}
void releaseRead(Thread t) {
synchronized(lock) {
while(readers.remove(t)); // remove this thread completely
}
}
boolean acquireWrite(Thread t) {
synchronized(lock) {
if(writer == null) return false;
writer = t;
}
while(readers.size() > 0) Thread.sleep(50); // give readers time to finish.
//They can't re-enter yet because we set the writer,
// if you attempt to acquire a write, future reads will be false until you're done
return true;
}
void releaseWrite(Thread t) {
synchronized(lock) {
if(t != writer) throw new IllegalArgumentException("Only writer can release itself");
writer = null;
}
}
}
Я мог бы использовать один семафор для блокировки всех чтения и записи, но я не думаю. что это очень хорошо работает.
Что касается производительности, НИКОГДА не думайте. ВСЕГДА измеряйте.
Учитывая это, java.util.concurrent.locks.ReentrantReadWriteLock
- это то, что вы ищете.
Если простой мьютекс для всего файла создает узкое место для производительности, а RandomAccessFile
не является потокобезопасным без мьютекса, тогда вам нужно взглянуть на альтернативы RandomAccessFile
.
Одна из альтернатив - отобразить файл в памяти как MappedBuffer
и использовать срезы буфера, чтобы позволить различным потокам обращаться к файлу, не мешая друг другу. Было бы легко реализовать блокировку одного устройства записи / нескольких считывателей на уровне детализации всего. Вы также можете пойти дальше и реализовать одновременное чтение и запись неперекрывающихся разделов файла, но это будет сложнее.
Я не удивлюсь, если узнаю, что кто-то где-то уже реализовал это как многоразовую библиотеку.