2

I am currently looking at implementing a session manager java class which provides functionality to read and refresh session tokens on demand. If the session token is being refreshed (i.e. being fetched from server) then session token readers should block until the refresh is complete. Similarly, a refresh request is blocked until any ongoing reads are not complete. Since session token read requests are quite frequent as compared to session token refresh requests, i have decided to use the ReentrantReadWriteLock to achieve the synchronisation between read and refresh. Here is how it looks:

  String refreshToken() {
         try{
             if (readWriteLock.writeLock().trylock()) {
                 //fetch session from server and store on disk
             }
        } finally {
              readWriteLock.writeLock().unlock();  
        }
        return readToken();
    }

   String readToken() {
         try {
         readWriteLock.readLock().lock();
         //read token from disk
         } finally {
               readWriteLock.readLock().unlock();
         }
    return token;
    }

}

My first attempt was to use a tryLock() on the write lock so that if it is locked for writing already then, tryLock() will return false and then acquire a read lock and block until the write lock is released thereby rescheduling read-blocked threads to complete the read. This logic works well for the case where multiple threads invoke refreshSession() at the same time thereby allowing only one thread to initiate the session token refresh whereas all other threads fall through and block on the read lock.

However, the logic above would fail if a thread had just acquired a read lock (by calling readToken()) and another thread invokes refreshToken() to acquire a write lock - the tryLock() would fail in this case missing the refresh request as a result.

As an alternative, I was looking at the readWriteLock.isWriteLocked() method that checks if any thread has acquired the write lock:

String refreshToken() {
    try{
        if (!readWriteLock.isWriteLocked()) {
            readWriteLock.writeLock().lock();
            //fetch session from server and store on disk
        } finally{
              readWriteLock.writeLock().unlock();  
        }
    }
    return readToken();
}

However, i do not have much experience with this method and not entirely sure about the synchronisation repercussions it would have since i want to ensure only one thread can acquire the write lock and subsequent requests fall through to a read. Any suggestions/ pointers would be most appreciated.

  • Makeing the `refreshToken()` a `synchronized` method can do the job, then only one thread at a time can access the `refreshToken()` part. Other threads will wait for the one which is currently 'in' the method to complete – Westranger Oct 08 '15 at 14:26
  • Well i would like to synchronize access to the session token between readers and writers. Besides there will be other methods on the class so i do not want to block the whole object by making this method synchronized. Hence the reason to use a lock. – user1623182 Oct 08 '15 at 14:33

1 Answers1

1

You can introduce a new ReentrantLock and tryLock there. The ReentrantLock promises only one writer while fails quickly if there exists a writer.

ReentrantLock lock = new ReentrantLock();

public String refreshToken() {
    if (lock.tryLock()) {
        readWriteLock.writeLock().lock();
        try {
            // fetch session from server and store on disk
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }
    return readToken();
}

So only one thread will ever try to write. And no threads can read while writing is progressing.

John Vint
  • 39,695
  • 7
  • 78
  • 108
  • Looks very promising - any other ideas that do not require an additional lock? – user1623182 Oct 08 '15 at 20:59
  • 1
    In the absence of any further comments/suggestions I am accepting this answer. – user1623182 Oct 09 '15 at 21:19
  • @user1623182 Thanks, and regarding your earlier comment, what did you have in mind? Without introducing some form of mutex I don't quite see how you can achieve what you want. For instance, your example won't protect indefinitely, imagine two threads call `isWriteLocked` at the same time and both succeed. In that case both will invoke `lock`. – John Vint Oct 12 '15 at 13:53
  • You are right, i had the same reservations about using isWriteLocked directly without using any mutex. However, i was simply checking if you had any other ideas/approaches you would have for this without using two locks. Do you consider the session refresh strategy i have adopted to be optimal given the API requirements (as originally outlined?). – user1623182 Oct 13 '15 at 11:29
  • I mean by using any other primitives or concurrency apis that i may not be aware of. – user1623182 Oct 13 '15 at 12:04
  • coming back to this issue after a lull but do you see a race condition in your suggestion above for the following scenario:where two threads call trylock() at the same time – user1623182 Nov 02 '15 at 17:06
  • The only type of race I can see is if the `readLock` is called before `writeLock`. – John Vint Nov 02 '15 at 17:12
  • Sorry, my comment above was half baked. The scenario i am referring to is: 1. Two threads T1 and T2 end up calling tryLock() at the same time. 2. T1.tryLock() returns true and before readWriteLock.writeLock().lock() can execute, T2.tryLock() fails and falls through to readToken(). 3. T2.readToken() continues to be scheduled and manages to acquire readLock() and before it can release the lock, readWriteLock.writeLock().lock() executes thereby causing an unwanted race condition. This is a very rare scenario but could happen possibly? – user1623182 Nov 03 '15 at 20:38