Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hierarchical mutex locks in Java

I want to be able to lock based on a filesystem hierarchy. For example:

Thread 1:

lock("/");
doStuff();
unlock();

Thread 2:

lock("/sub/foo");
doStuff();
unlock();

Thread 3:

lock("/sub/bar");
doStuff();
unlock();

If Thread 1 acquires the lock first then threads 2 and 3 will be blocked until Thread 1 unlocks. However, if Thread 2 acquires the lock first, then Thread 3 should be able to execute at the same time as Thread 2. The general rule is that if there is a lock on a parent directory, then the thread must block.

Does Java have anything built-in that can help solve this? I want to avoid storing a lock per directory because there will be hundreds of thousands of directories.

like image 985
Scott Frazer Avatar asked Feb 15 '12 21:02

Scott Frazer


People also ask

What is mutex lock in Java?

A mutex (or mutual exclusion) is the simplest type of synchronizer – it ensures that only one thread can execute the critical section of a computer program at a time. To access a critical section, a thread acquires the mutex, then accesses the critical section, and finally releases the mutex.

What is lock hierarchy?

The locks are arranged in a hierarchy to prevent a deadlock between functions on the processor(s). A function on a processor can request unconditionally only those locks that are higher in the hierarchy than the locks it currently holds, thus preventing deadlocks.

What are reentrant locks in Java?

A ReentrantLock is owned by the thread last successfully locking, but not yet unlocking it. A thread invoking lock will return, successfully acquiring the lock, when the lock is not owned by another thread. The method will return immediately if the current thread already owns the lock.


1 Answers

I'd store the directory paths in a tree like this:

- /
 - sub
  - foo
  - bar

Whenever you need to lock anything in that tree you go down from the root and acquire a read-lock on everything except the target node itself. The target node gets a write lock.

This scheme guarantees you deadlock-freedom and stability of the relevant parts of the tree.

I don't see a particular problem storing hundreds of thousands of locks. This is probably going to waste maybe 100 bytes of RAM per lock. But it simplifies the architecture. Did you measure if it actually is a problem?

As an alternative you can have a map from path to lock. All operations on that dictionary must be synchronized by callers. This allows you to lazily initialize locks. You can also periodically garbage-collect unused locks by first taking a write-lock on the root which quiesces all operations. Once everything is quiet you discard all non-root locks.

like image 121
usr Avatar answered Sep 29 '22 02:09

usr