Reputation: 131
Code like this...
public void beforeUpdated(Log log){
synchronized(this){
query(log);
merge(log);
persist(log);
}
}
This method is running under the multi-thread environment.The logs' CRUD must be an atomic operation.But only log of same id(log.getUuid()) need be synchronized.If I lock all the operation,it must have been a poor performance.I just hope that the logs of the same id lock under an atomic operation.How should I do like this?Please help me if you have any idea,thanks.
Upvotes: 4
Views: 458
Reputation: 13779
One suggestion is to do something like:
class Log
{
private static final WeakHashMap<String, Object> LOCKS =
new WeakHashMap<String, Object>();
private final String uuid;
public Log(String uuid)
{
this.uuid = uuid;
}
public Object getLock()
{
synchronized (LOCKS)
{
Object lock = LOCKS.get(uuid);
if (lock == null)
{
lock = new Object();
LOCKS.put(uuid, lock);
}
return lock;
}
}
}
and use it as:
public void beforeUpdated(Log log)
{
synchronized (log.getLock())
{
query(log);
merge(log);
persist(log);
}
}
If the bottle-neck of acquiring the lock from the static weak map bothers you, you can try using something like Guava MapMaker to construct a concurrent weak hash map.
Upvotes: 0
Reputation: 1760
I encountered this situation a few times. What you need is a singleton LockFactory that is actually a dictionary of weak references for lock objects. the code should be something like:
class LockFactory {
private LockFactory() {}
private LockFactory instance = null;
public static LockFactory getInstance() {
if (this.instance == null)
this.instance = new LockFactory();
return this.instance;
}
private int _last_check_size = 0;
private int _cleanup_size = 1000;
private Map<String, WeakReference> weakRefDictionary = new HashMap<String, WeakReference>();
public object getLock(String id) {
synchronized(this) {
if (!this.weakRefDictionary.containsKey(id))
this.weakRefDictionary.put(id, new WeakReference(null));
Object lock = this.weakRefDictionary.get(id).Target;
if (lock == null) {
lock = new Object();
this.weakRefDictionary.get(id).Target = lock;
}
if (this.weakRefDictionary.size() > this._last_check_size + this._cleanup_size)
this._do_cleanup();
return lock;
}
}
public void _do_cleanup() {
synchronized(this) {
Iterator<Map.Entry<String, WeakReference>> iter = this.weakRefDictionary.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String,WeakReference> entry = iter.next();
if (entry.getValue().get() == null) {
iter.remove();
}
}
this._last_check_size = this.weakRefDictionary.size();
}
}
}
Now to use in your case just do:
public void beforeUpdated(Log log){
synchronized(LockFactory.getInstance().getLock(log.getUuid())){
query(log);
merge(log);
persist(log);
}
}
Upvotes: 5
Reputation: 37940
You could maintain a HashMap
that maps the log ids that have been encountered so far to some Object
, and synchronize on the Object
that belongs to the id of the log being written to. Note that reads and writes to the HashMap
must be synchronized on the HashMap
itself.
Upvotes: 1