2013-02-01 101 views
1

我有一個同步練習,我需要同步一個讀取方法,以便任何數量的線程可以執行它,只要沒有寫入方法正在執行。這必須從頭開始,所以我不能使用java.util.concurrent.locks等。Java同步:無阻塞鎖定

爲此,我需要某種機制來防範,但不阻止讀取方法,所以讀取線程被寫入而不是其他讀取阻塞。我不能使用普通的鎖定,因爲在read方法中調用lock方法會導致其他讀取線程等待。當一個線程在write()中時,沒有其他線程必須輸入read()或write() 當一個線程在read()中時,其他線程不必輸入write() ),但他們可能會進入閱讀()

我已經嘗試建立一對自制鎖來處理這個問題。 WriteLock是一個相當標準的reenterant鎖,除了在正在執行讀操作時它會被阻塞(使用讀計數器) 如果遍歷write(),ReadLock應該只會導致線程等待。否則,它應該簡單地允許線程執行其業務並增加WriteLocks計數器。

代碼:

package sync; 

public class SyncTest { 
    Long testlong = new Long(0L); 
    int reads = 0; 
    int writes = 0; 
    WriteLock w = new WriteLock(); 
    ReadLock r = new ReadLock(w); 

    public SyncTest() { 
     // TODO Auto-generated constructor stub 
    } 

    public static void main(String args[]){ 

     final SyncTest s = new SyncTest(); 

     for(int i = 0 ; i<3 ; i++){ //Start a number of threads to attack SyncTest 
      final int ifinal = i; 
      new Thread(){ 
       int inc = ifinal; 
       @Override 
       public void run() { 
        System.out.println("Starting "+inc); 
        long starttime = System.currentTimeMillis(); 
        try { 
        while(System.currentTimeMillis()-starttime < 10){ 

         if (inc < 2){ 

          s.readLong(); 

         }else{ 
          s.writeLong(inc+1); 
         } 
        } 
        System.out.println(inc + " done"); 
        if(inc == 0){ 
         Thread.sleep(1000); 
         System.out.println(s.reads+" "+s.writes); 
        } 
        } catch (InterruptedException e) { 
         // TODO Auto-generated catch block 
         e.printStackTrace(); 
        } 

        // TODO Auto-generated method stub 

       } 
       @Override 
       public String toString() { 
        // TODO Auto-generated method stub 
        return "Thread "+inc+" "+super.toString(); 
       } 



      }.start(); 
     } 
    } 

    public Long readLong() throws InterruptedException{ 

     Long l; 
     r.lock(); //Lock for reading 
     //System.out.println("Read "+reads); 
     l = testlong; 
     reads++; 
     r.unlock(); //Unlock after reading 
     return l; 
     } 

    public void writeLong(int i) throws InterruptedException{ 

     w.lock(); //Lock for writing 
     //System.out.println("Write "+writes); 
     int curreads = reads; 
     int curwrites = writes; 
     testlong = testlong + i; 
     writes++; 

     Thread.sleep(100); //Simulate long write 
     if(curreads != reads){ 
      System.out.println("Reads did not lock"); 
     } 

     if(curwrites+1 != writes){ 
      System.out.println("Writes did not lock"); 
     } 
     w.unlock(); //Unlock writing 
    } 

    protected class WriteLock{ 
     boolean isLocked = false; 
     Thread lockedBy = null; 
     int lockedCount = 0; 
     int readers = 0; //The number of readers currently through the reading lock. 

     public synchronized void lock() throws InterruptedException { 
      System.out.println("Locking: "+Thread.currentThread()); 
      Thread callingThread = Thread.currentThread(); 
      while ((isLocked && lockedBy != callingThread) || readers > 0) { //Wait if locked or readers are in read() 
       wait(); 
      } 
      isLocked = true; 
      lockedCount++; 
      lockedBy = callingThread; 
      System.out.println("Is locked: "+Thread.currentThread()); 
     } 

     public synchronized void unlock() { 
      //System.out.println("Unlocking: "+Thread.currentThread()); 
      if (Thread.currentThread() == this.lockedBy) { 
       lockedCount--; 

       if (lockedCount == 0) { 
        System.out.println("Is unlocked: "+Thread.currentThread()); 
        isLocked = false; 
        notify(); 
       } 
      } 
     } 

    } 

    protected class ReadLock{ 
     WriteLock lock; 

     public ReadLock(WriteLock lock) { 
      super(); 
      this.lock = lock; 
     } 

     public synchronized void lock() throws InterruptedException { //If write() then wait 
      System.out.println("Waiting to read: "+Thread.currentThread()); 
      Thread callingThread = Thread.currentThread(); 
      while (lock.isLocked && lock.lockedBy != callingThread) { 
       wait(); 
      } 
      lock.readers++; //Increment writelocks readers 
      System.out.println("Reading: "+Thread.currentThread()); 

     } 

     public synchronized void unlock() { 
      lock.readers--; //Subtract from writelocks readers 
      notify(); 
     } 

    } 

} 

這不但是工作,讀鎖的工作原理到目前爲止,它鎖定當一個線程正在寫的讀者,但是當WriteLock解鎖它不釋放出來,據我可以告訴。

這是不是在概念上合理,或者有什麼我不明白與監視器?或者是其他東西?

+2

我會鼓勵你換一個顯示器'嘗試終於{}鎖定你的鎖。如果在鎖定過程中發生任何事情,您的代碼將無法解鎖。 – Gray

+0

非常真實。但這只是一個簡短的編譯,我不想混淆代碼。沒有錯誤被拋出。 –

回答

1

您的ReadLock和WriteLock正在同步不同的對象,並在不同的對象上調用wait和notify。

這允許ReadLock在WriteLock驗證它們時修改WriteLock中的計數。這也會導致不同的鎖不會從等待的呼叫中喚醒。

如果修改ReadLock使用WriteLock因爲你得到更好的效果(我沒有檢查,如果這是唯一的問題)

protected class ReadLock{ 
    WriteLock lock; 

    public ReadLock(WriteLock lock) { 
     super(); 
     this.lock = lock; 
    } 

    public void lock() throws InterruptedException { //If write() then wait 
     synchronized (lock) { 
      System.out.println("Waiting to read: "+Thread.currentThread()); 
      Thread callingThread = Thread.currentThread(); 
      while (lock.isLocked && lock.lockedBy != callingThread) { 
       lock.wait(); 
      } 
      lock.readers++; //Increment writelocks readers 
      System.out.println("Reading: "+Thread.currentThread()); 
     } 
    } 

    public void unlock() { 
     synchronized (lock) { 
      lock.readers--; //Subtract from writelocks readers 
      lock.notify(); 
     } 
    } 

} 
12

(回答的問題是在作爲一個運動的它而言編輯之前。)

這聽起來像你想有一個ReadWriteLock實現。

ReadWriteLock維護一對相關的鎖,一個用於只讀操作,另一個用於寫入。只要沒有寫入器,讀取鎖可以由多個讀取器線程同時保存。寫鎖定是獨佔的。

一個實現是ReentrantReadWriteLock

對於併發性,在嘗試實現自己的代碼之前,總是有必要查看現有庫(java.util.concurrent等)。如果你和我一樣,即使你能在併發性方面做到正確的,它也不會像專家寫的代碼那樣高效......當然,這一切都是以工作開始的;)

+0

除非你想實現讀寫器鎖定作爲學習練習,否則你應該使用Jon提供的java庫附帶的那個。 – Bill

+0

這是一個惡作劇......但upvote仍然:) –

+0

@MartinNielsen:你應該在你的問題中這麼說。你說你有一個同步問題。 ReadWriteLock是解決同步問題的方法。你沒有說要從最初的原則來執行它。 –