2010-05-21 65 views
6

经过一些严重的googleing后,我发现RandomAccessFile类不是线程安全的。现在我可以使用一个信号量来锁定所有的读写操作,但我认为这并不是很好。从理论上讲,应该可以一次完成多次读取和一次写入。 我怎样才能在Java中做到这一点?它有可能吗?Java:线程安全的RandomAccessFile

谢谢!

+0

这取决于您如何从文件中读取和写入,但来自java.util.concurrent的同步原语在现代JVM上表现得非常好。 – 2010-05-21 13:02:16

+0

我明白如果您尝试使用来自不同线程的相同RandomAccessFile,但您确实需要同时进行多个读取?我不是专家,但在大多数情况下,硬件将无法同时提供多个读取(我不知道用于高端磁盘阵列)。 – 2010-05-21 14:01:18

+0

它将与RAID阵列一起使用。 另外:当数据来自缓存时,它可以并行检索。 – 2010-05-21 15:24:14

回答

1

文件的部分锁定是一个复杂的业务,很多操作系统都避免这样做。但是,如果你坚持这样做,一种方法是设计自己的锁定机制对象,该对象记录文件的哪些部分被锁定。基本上,在读取或写入对象之前,必须为文件的特定字节范围请求锁定。如果它们在字节范围内完全重叠,则锁被认为是冲突的。读取和写入锁定的处理方式不同:读取可以安全地与任意数量的读取锁定重叠,但写入锁定必须与其他锁定(读取或写入)重叠。如果无法获得锁定,是否等待或中止,以及是否在写入等待时阻止读取,但只有您可以回答有关您的应用程序的问题,这里有很多问题。

鉴于此复杂性,最好锁定整个文件。检查一下你是否得到了足够的性能 - 并且不要忘记,只要没有写入,就可以一次允许多次读取。

+0

嗯有趣的想法。我有一个readblock(int nr)和一个写块(int nr)。现在我可以有一个数组Semaphore [] locks = new Semaphore [10]; 然后在readblock/writeblock()我可以锁[nr%10] .acquireUninterruptibly() 不能想到任何问题。 虽然我仍然认为底层的RandomAccessFile会失败并发访问 – 2010-05-21 13:32:06

+0

制作锁定对象的目的是为了防止存在并发访问。您必须编写代码,以便所有线程在访问文件之前都必须从锁定对象获取锁定,并在完成后释放它。 – DJClayworth 2010-05-21 20:45:30

7

我可以用一个信号量锁定所有 读取和写入,但我不认为 执行得非常好。

关于表现,从来没有想过。总是测量。

这就是说,java.util.concurrent.locks.ReentrantReadWriteLock是你在找什么。

+1

是的,但后来我会做2个或更多的并发读取,RandomAccessFile不会处理。 请参阅:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4193259('评估'部分) – 2010-05-21 13:04:23

+0

因此,您不能使用读取锁定,以便您可以使用同步。 – EJP 2010-05-22 01:17:00

1

请考虑这种方法 - 它允许无限的读者,并且当作者想要写作时,它会等待当前读者完成写作。

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

} 
+0

看起来像一个java.util.concurrent.locks.ReentrantReadWriteLock实现? 但仍然:我需要一种方式来多次读取和/或写入才能成功。而RandomAccessFile不允许多次读取。 – 2010-05-21 13:23:31

+0

这允许多次读取。只是不是多次写入。 – corsiKa 2010-05-21 13:26:56

+0

是的,但RandomAccessFile不允许多次读取。 我正在寻找其他一些课程。 – 2010-05-21 13:40:54

1

如果对整个文件一个简单的互斥体是要给你一个性能瓶颈,并RandomAccessFile是不是线程安全的不互斥,那么你就需要在替代看RandomAccessFile

一种替代方法是将文件映射到内存中作为MappedBuffer,并使用缓冲区片来允许不同线程访问文件而不会相互干扰。单个作者/多读者锁定在整个粒度将很容易实现。您还可以进一步实施并发读取和写入文件的非重叠部分,但这会更复杂。

我不会惊讶地听到有人在某处已经实现了这个可重用的库。