Bobby Tang
Bobby Tang

Reputation: 131

How to add a lock in this situation?

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

Answers (3)

Binil Thomas
Binil Thomas

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

idanzalz
idanzalz

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

Aasmund Eldhuset
Aasmund Eldhuset

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

Related Questions