User Tools

Site Tools


java:read_write_locks

Reentrant Read-Write Locks

Εάν ένα σύνολο από threads διαβάζουν ένα σύνολο μεταβλητών χωρίς να προσπαθούν να μεταβάλλουν τις τιμές τους τότε το συγκεκριμένο τμήμα του κώδικα δεν είναι κρίσιμο. Η κρισιμότητα του κώδικα εισάγεται όταν εκτός από ανάγνωση μεταβλητών επιχειρούμε να μεταβάλλουμε και τις τιμές τους.

Με βάση την παραπάνω διαπίστωση μπορούμε να κάνουμε τις εξής παραδοχές:

  1. Δύο ή περισσότερα threads μπορούν να έχουν παράλληλα πρόσβαση σε ένα κρίσιμο τμήμα εάν επιτελούν μόνο ανάγνωση.
  2. Μόνο ένα thread μπορεί να έχει πρόσβαση σε ένα κρίσιμο τμήμα εάν επιτελεί εγγραφή μεταβλητών.

Παρακάτω παρατίθεται ο κώδικας μίας κλειδαριάς η οποία είναι reentrant και διακρίνει το κλείδωμα για ανάγνωση σε σχέση με το κλείδωμα για εγγραφή

ReentrantReadWriteLock.java
public class ReentrantReadWriteLock{
 
  private Map<Thread, Integer> readingThreads = new HashMap<Thread, Integer>();
 
   private int writeAccesses    = 0;
   private int writeRequests    = 0;
   private Thread writingThread = null;
 
  public synchronized void lockRead() throws InterruptedException{
    Thread callingThread = Thread.currentThread();
    while(! canGrantReadAccess(callingThread)){
      wait();
    }
 
    readingThreads.put(callingThread,
     (getReadAccessCount(callingThread) + 1));
  }
 
  private boolean canGrantReadAccess(Thread callingThread){
    if( isWriter(callingThread) ) return true;
    if( hasWriter()             ) return false;
    if( isReader(callingThread) ) return true;
    if( hasWriteRequests()      ) return false;
    return true;
  }
 
  public synchronized void unlockRead(){
    Thread callingThread = Thread.currentThread();
    if(!isReader(callingThread)){
      throw new IllegalMonitorStateException("Calling Thread does not" +
        " hold a read lock on this ReentrantReadWriteLock");
    }
    int accessCount = getReadAccessCount(callingThread);
    if(accessCount == 1){ readingThreads.remove(callingThread); }
    else { readingThreads.put(callingThread, (accessCount -1)); }
    notifyAll();
  }
 
  public synchronized void lockWrite() throws InterruptedException{
    writeRequests++;
    Thread callingThread = Thread.currentThread();
    while(! canGrantWriteAccess(callingThread)){
      wait();
    }
    writeRequests--;
    writeAccesses++;
    writingThread = callingThread;
  }
 
  public synchronized void unlockWrite() throws InterruptedException{
    if(!isWriter(Thread.currentThread()){
      throw new IllegalMonitorStateException("Calling Thread does not" +
        " hold the write lock on this ReentrantReadWriteLock");
    }
    writeAccesses--;
    if(writeAccesses == 0){
      writingThread = null;
    }
    notifyAll();
  }
 
  private boolean canGrantWriteAccess(Thread callingThread){
    if(isOnlyReader(callingThread))    return true;
    if(hasReaders())                   return false;
    if(writingThread == null)          return true;
    if(!isWriter(callingThread))       return false;
    return true;
  }
 
  private int getReadAccessCount(Thread callingThread){
    Integer accessCount = readingThreads.get(callingThread);
    if(accessCount == null) return 0;
    return accessCount.intValue();
  }
 
  private boolean hasReaders(){
    return readingThreads.size() > 0;
  }
 
  private boolean isReader(Thread callingThread){
    return readingThreads.get(callingThread) != null;
  }
 
  private boolean isOnlyReader(Thread callingThread){
    return readingThreads.size() == 1 &&
           readingThreads.get(callingThread) != null;
  }
 
  private boolean hasWriter(){
    return writingThread != null;
  }
 
  private boolean isWriter(Thread callingThread){
    return writingThread == callingThread;
  }
 
  private boolean hasWriteRequests(){
      return this.writeRequests > 0;
  }
 
}
java/read_write_locks.txt · Last modified: 2016/05/15 18:59 (external edit)