2010-05-21 64 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,並使用緩衝區片來允許不同線程訪問文件而不會相互干擾。單個作者/多讀者鎖定在整個粒度將很容易實現。您還可以進一步實施併發讀取和寫入文件的非重疊部分,但這會更復雜。

我不會驚訝地聽到有人在某處已經實現了這個可重用的庫。