经过一些严肃的googleing后,我发现RandomAccessFile-class不是线程安全的。现在我可以使用一个信号量来锁定所有的读写操作,但我不认为它表现得非常好。理论上,应该可以一次执行多次读取和一次写入。 我怎么能用Java做到这一点?它有可能吗?
谢谢!
答案 0 :(得分:7)
我可以使用一个信号量来锁定所有信号量 读写,但我不认为 表现得非常好。
关于表现,永远不要想。总是衡量。
那就是说,java.util.concurrent.locks.ReentrantReadWriteLock
正是你要找的。 p>
答案 1 :(得分:2)
文件的部分锁定是一项复杂的业务,许多操作系统都避免这样做。但是,如果您坚持这样做,一种方法是设计自己的锁定机制对象,记录文件的哪些部分被锁定。基本上在读取或写入对象之前,必须请求锁定文件的特定字节范围。如果锁在字节范围内完全重叠,则认为它们发生冲突。读写锁的处理方式不同:读取可以安全地与任意数量的读锁重叠,但写锁必须与其他锁,读或写重叠。如果你无法获得锁定,是否等待或中止,以及是否在写入等待时阻止读取,有很多问题,但只有你可以回答它们关于你的应用程序。
鉴于此复杂性,锁定整个文件可能更好。检查您是否获得足够的性能 - 并且不要忘记只要没有写入就可以一次允许多次读取。
答案 2 :(得分:2)
考虑这种方法 - 它允许无限的读者,当作家想要写作时,它等待当前读者完成写作。
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;
}
}
}
答案 3 :(得分:2)
如果整个文件上的简单互斥锁会给您带来性能瓶颈,并且RandomAccessFile
在没有互斥锁的情况下不是线程安全的,那么您需要查看RandomAccessFile
的替代方法。
另一种方法是将文件作为MappedBuffer
映射到内存中,并使用缓冲区片段允许不同的线程访问文件而不会相互干扰。单个编写器/多个读卡器锁定在整个粒度将很容易实现。您还可以进一步实现并发读取和写入文件的非重叠部分,但这会更复杂。
听到某个人已经将其作为可重用的库实现,我不会感到惊讶。
答案 4 :(得分:0)
java.nio.channels.FileChannel
正是这样做的,即安全的并发读取和单线程写入。