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.