在这种情况下如何添加锁?

4

像这样的代码...

public void beforeUpdated(Log log){
    synchronized(this){
       query(log);
       merge(log);
       persist(log);
    }
}

这个方法是在多线程环境下运行的。日志的CRUD必须是原子操作。但是只有相同id(log.getUuid())的日志需要进行同步。如果我锁定所有的操作,那么性能肯定很差。我只希望相同id的日志在一个原子操作下锁定。如何实现这样的操作?如果您有任何想法,请帮助我,谢谢。


2
我会将所有内容添加到单线程后台线程池中。如果速度足够快,您就不需要任何额外的锁定。 - Peter Lawrey
3个回答

5
我遇到过这种情况几次。你需要的是一个单例的LockFactory,实际上是一个弱引用锁对象的字典。代码应该像这样:
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();
        }
    }
}

现在只需要按照以下步骤使用:

1. 在您的案例中执行:

public void beforeUpdated(Log log){
    synchronized(LockFactory.getInstance().getLock(log.getUuid())){
       query(log);
       merge(log);
       persist(log);
    }
}

当锁没有被锁定时,由于没有其他强引用指向它,锁几乎会立即被丢弃。 - Peter Lawrey
1
在使用对象时,由于该对象被保留在栈中直至其被释放,因此存在引用。 - idanzalz
@idanzalz: 又有一个问题,<code>if (this.WeakRefDictionary.Count > this._last_check_size + this._cleanup_size)</code>的意思是什么?为什么需要一个last_check_size?为什么不是'count > cleanup_size'?这段代码的目的是防止堆栈溢出吗? - Bobby Tang
@Peter 我不确定你是否可以在这里使用 WeakHashMap,因为它只会弱引用 __key__,而不是目标对象。也许有一种方法可以重新编写所有内容以正确使用它,但我没有找到... 不过,我还没有仔细看过。 - toto2
@toto2,只有键被外部使用。值是一个虚拟的锁对象。 - Peter Lawrey
显示剩余11条评论

1
你可以维护一个 HashMap,将到目前为止遇到的日志 id 映射到某个 Object 上,并在正在写入的日志所属的 Object 上进行同步。请注意,对 HashMap 的读取和写入必须在 HashMap 本身上进行同步。

你需要一个HashMap,这样你就可以执行get()来查找匹配的对象。Set不会给你匹配的对象。 - Peter Lawrey

0

一个建议是这样做:

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;
        }
    }
}

并将其用作:

public void beforeUpdated(Log log) 
{
    synchronized (log.getLock()) 
    {
       query(log);
       merge(log);
       persist(log);
    }
}

如果从静态弱映射中获取锁的瓶颈困扰着您,您可以尝试使用类似Guava MapMaker这样的工具来构建并发弱哈希映射。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接