====== Reentrant Read-Write Locks ====== Εάν ένα σύνολο από threads διαβάζουν ένα σύνολο μεταβλητών χωρίς να προσπαθούν να μεταβάλλουν τις τιμές τους τότε το συγκεκριμένο τμήμα του κώδικα δεν είναι κρίσιμο. Η κρισιμότητα του κώδικα εισάγεται όταν εκτός από ανάγνωση μεταβλητών επιχειρούμε να μεταβάλλουμε και τις τιμές τους. Με βάση την παραπάνω διαπίστωση μπορούμε να κάνουμε τις εξής παραδοχές: - Δύο ή περισσότερα threads μπορούν να έχουν παράλληλα πρόσβαση σε ένα κρίσιμο τμήμα εάν επιτελούν μόνο ανάγνωση. - Μόνο ένα thread μπορεί να έχει πρόσβαση σε ένα κρίσιμο τμήμα εάν επιτελεί εγγραφή μεταβλητών. Παρακάτω παρατίθεται ο κώδικας μίας κλειδαριάς η οποία είναι reentrant και διακρίνει το κλείδωμα για ανάγνωση σε σχέση με το κλείδωμα για εγγραφή public class ReentrantReadWriteLock{ private Map readingThreads = new HashMap(); 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; } }