经过一些严重的googleing后,我发现RandomAccessFile类不是线程安全的。现在我可以使用一个信号量来锁定所有的读写操作,但我认为这并不是很好。从理论上讲,应该可以一次完成多次读取和一次写入。 我怎样才能在Java中做到这一点?它有可能吗?Java:线程安全的RandomAccessFile
谢谢!
经过一些严重的googleing后,我发现RandomAccessFile类不是线程安全的。现在我可以使用一个信号量来锁定所有的读写操作,但我认为这并不是很好。从理论上讲,应该可以一次完成多次读取和一次写入。 我怎样才能在Java中做到这一点?它有可能吗?Java:线程安全的RandomAccessFile
谢谢!
文件的部分锁定是一个复杂的业务,很多操作系统都避免这样做。但是,如果你坚持这样做,一种方法是设计自己的锁定机制对象,该对象记录文件的哪些部分被锁定。基本上,在读取或写入对象之前,必须为文件的特定字节范围请求锁定。如果它们在字节范围内完全重叠,则锁被认为是冲突的。读取和写入锁定的处理方式不同:读取可以安全地与任意数量的读取锁定重叠,但写入锁定必须与其他锁定(读取或写入)重叠。如果无法获得锁定,是否等待或中止,以及是否在写入等待时阻止读取,但只有您可以回答有关您的应用程序的问题,这里有很多问题。
鉴于此复杂性,最好锁定整个文件。检查一下你是否得到了足够的性能 - 并且不要忘记,只要没有写入,就可以一次允许多次读取。
嗯有趣的想法。我有一个readblock(int nr)和一个写块(int nr)。现在我可以有一个数组Semaphore [] locks = new Semaphore [10]; 然后在readblock/writeblock()我可以锁[nr%10] .acquireUninterruptibly() 不能想到任何问题。 虽然我仍然认为底层的RandomAccessFile会失败并发访问 – 2010-05-21 13:32:06
制作锁定对象的目的是为了防止存在并发访问。您必须编写代码,以便所有线程在访问文件之前都必须从锁定对象获取锁定,并在完成后释放它。 – DJClayworth 2010-05-21 20:45:30
我可以用一个信号量锁定所有 读取和写入,但我不认为 执行得非常好。
关于表现,从来没有想过。总是测量。
这就是说,java.util.concurrent.locks.ReentrantReadWriteLock
是你在找什么。
是的,但后来我会做2个或更多的并发读取,RandomAccessFile不会处理。 请参阅:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4193259('评估'部分) – 2010-05-21 13:04:23
因此,您不能使用读取锁定,以便您可以使用同步。 – EJP 2010-05-22 01:17:00
请考虑这种方法 - 它允许无限的读者,并且当作者想要写作时,它会等待当前读者完成写作。
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不允许多次读取。 – 2010-05-21 13:23:31
这允许多次读取。只是不是多次写入。 – corsiKa 2010-05-21 13:26:56
是的,但RandomAccessFile不允许多次读取。 我正在寻找其他一些课程。 – 2010-05-21 13:40:54
如果对整个文件一个简单的互斥体是要给你一个性能瓶颈,并RandomAccessFile
是不是线程安全的不互斥,那么你就需要在替代看RandomAccessFile
。
一种替代方法是将文件映射到内存中作为MappedBuffer
,并使用缓冲区片来允许不同线程访问文件而不会相互干扰。单个作者/多读者锁定在整个粒度将很容易实现。您还可以进一步实施并发读取和写入文件的非重叠部分,但这会更复杂。
我不会惊讶地听到有人在某处已经实现了这个可重用的库。
这取决于您如何从文件中读取和写入,但来自java.util.concurrent的同步原语在现代JVM上表现得非常好。 – 2010-05-21 13:02:16
我明白如果您尝试使用来自不同线程的相同RandomAccessFile,但您确实需要同时进行多个读取?我不是专家,但在大多数情况下,硬件将无法同时提供多个读取(我不知道用于高端磁盘阵列)。 – 2010-05-21 14:01:18
它将与RAID阵列一起使用。 另外:当数据来自缓存时,它可以并行检索。 – 2010-05-21 15:24:14