In our system, we have a method that will do some work when it\'s called with a certain ID:
public void doWork(long id) { /* ... */ }
Now, this
You could create a list or set of active ids and use wait and notify:
List<Long> working;
public void doWork(long id) {
synchronized(working)
{
while(working.contains(id))
{
working.wait();
}
working.add(id)//lock
}
//do something
synchronized(working)
{
working.remove(id);//unlock
working.notifyAll();
}
}
Problems solved:
Problems there:
You can try something with a ReentrantLock, such that you have a Map<Long,Lock>. Now after lock.release() You can test lock.hasQueuedThreads(). If that returns false you can remove it from the Map.
I might be late to the game, but this solution isn't leaking any memory and you don't have to remember to do any lock releases:
Synchronizer<AccountId> synchronizer = new Synchronizer();
...
// first thread - acquires "lock" for accountId accAAA
synchronizer.synchronizeOn(accountId("accAAA"), () -> {
long balance = loadBalance("accAAA")
if (balance > 10_000) {
decrementBalance("accAAA", 10_000)
}
})
...
// second thread - is blocked while first thread runs (as it uses the same "lock" for accountId accAAA)
synchronizer.synchronizeOn(accountId("accAAA"), () -> {
long balance = loadBalance("accAAA")
if (balance > 2_000) {
decrementBalance("accAAA", 2_000)
}
})
...
// third thread - won't be blocked by previous threads (as it is for a different accountId)
synchronizer.synchronizeOn(accountId("accXYZ"), () -> {
long balance = loadBalance("accXYZ")
if (balance > 3_500) {
decrementBalance("accXYZ", 3_500)
}
})
to use it you just add a dependency:
compile 'com.github.matejtymes:javafixes:1.3.0'
This is where I would use a canonicalizing map, which takes your long input and returns a canonical Long object that you can then use to synchronize. I've written about canonicalizing maps here; simply replace String by Long (and to make your life easier, let it take a long as a parameter).
Once you have the canonicalizing map, you'd write your lock-guarded code like this:
Long lockObject = canonMap.get(id);
synchronized (lockObject)
{
// stuff
}
The canonicalizing map would ensure that the same lockObject is returned for the same ID. When there are no active references to lockObject, they will be eligible for garbage collection, so you won't fill up memory with needless objects.