Package net.dapete.locks


package net.dapete.locks

This package contains classes for key-based locking, as in a way to obtain instances of Lock or ReadWriteLock which are identified by a key. These locks are guaranteed to by different for each key and will be kept as long as they are referenced.

Obtaining instances
Type of Lock Method(s) Type returned
ReentrantLock Locks.reentrant()
Locks.reentrant(Class)
Locks.reentrant(boolean)
Locks.reentrant(boolean, Class)
ReentrantLocks
any implementation of Lock Locks.withSupplier(Supplier) Locks
ReentrantReadWriteLock ReadWriteLocks.reentrant()
ReadWriteLocks.reentrant(Class)
ReadWriteLocks.reentrant(boolean)
ReadWriteLocks.reentrant(boolean, Class)
ReentrantReadWriteLocks
any implementation of ReadWriteLock ReadWriteLocks.withSupplier(Supplier) ReadWriteLocks
  • All implementations use generics for the type of the key as well as the type of lock, if this is not fixed (as with ReentrantLocks and ReentrantReadWriteLocks).
  • The Class parameter on the reentrant(Class) and reentrant(boolean, Class) methods is the Class of the key type. This is a shortcut for readability if the compiler does not automatically detect it, for example:
    Locks.reentrant(String.class)
  • The boolean parameter on the reentrant(boolean) and reentrant(boolean, Class) sets the fairness policy of the reentrant lock instances used. See also the constructors ReentrantLock(boolean) and ReentrantReadWriteLock(boolean).
  • The withSupplier(Supplier) methods allow for any implementation of Lock or ReadWriteLock to be used. Just use the constructor as the Supplier, for example:
    Locks.withSupplier(SuperEpicLock::new)

Examples

Following the same pattern as described in the JDK documentation for Lock, using Locks should look similar to this:

 public class LocksExample {

 private final ReentrantLocks<String> locks = Locks.reentrant();

     public void doSomething(String url) {
         final var lock = locks.lock(url);
         try {
             // do something with the URL
         } finally {
             lock.unlock();
         }
     }

 }
 

It is important to keep the lock in a local variable while it is being used. It is stored in a WeakReference, so it could be removed by the garbage collector at any time while it is not referenced.

An alternative way which may be useful if the lock is not always used or used multiple times would be to split the final var lock = … line in two:

 final var lock = locks.get(url);
 lock.lock();
 

For ReadWriteLocks it is similar to the first example:

 public class ReadWriteLocksExample {

     private final ReentrantReadWriteLocks<String> locks = ReadWriteLocks.reentrant();

     public void doSomethingRead(String url) {
         final var lock = locks.readLock(url);
         try {
             // do something with the URL
         } finally {
             lock.readLock().unlock();
         }
     }

     public void doSomethingWrite(String url) {
         final var lock = locks.writeLock(url);
         try {
             // do something with the URL
         } finally {
             lock.writeLock().unlock();
         }
     }

 }
 

Again the final var lock = … lines could be split, which may be useful if both read and write locks are used in the method.