简单的基于 Java 名称的锁?

2022-08-31 14:30:38

MySQL有一个方便的功能:

SELECT GET_LOCK("SomeName")

这可用于为应用程序创建简单但非常具体的基于名称的锁。但是,它需要数据库连接。

我有很多情况,比如:

someMethod() {
    // do stuff to user A for their data for feature X
}

简单地同步此方法是没有意义的,因为,例如,如果在此期间为用户 B 调用此方法,则用户 B 不需要等待用户 A 完成才能启动,只需等待用户 A 和功能 X 组合的操作。

使用MySql锁,我可以做这样的事情:

someMethod() {
    executeQuery("SELECT GET_LOCK('userA-featureX')")
    // only locked for user A for their data for feature X
    executeQuery("SELECT RELEASE_LOCK('userA-featureX')")
}

由于Java锁定是基于对象的,因此似乎我需要创建一个新对象来表示此锁定的情况,然后将其放在某个位置的静态缓存中,以便所有线程都可以看到它。然后,针对该情况锁定的后续请求将在缓存中找到锁定对象并获取其锁定。我试图创建这样的东西,但是锁缓存本身需要同步。此外,很难检测锁定对象何时不再使用,以便将其从缓存中删除。

我已经看过Java并发包,但没有什么能突出的,能够处理这样的事情。有没有一种简单的方法来实现这一点,或者我从错误的角度来看待这个问题?

编辑:

为了澄清,我不打算提前创建预定义的锁池,我想按需创建它们。我所想到的一些伪代码是:

LockManager.acquireLock(String name) {
    Lock lock;  

    synchronized (map) {
        lock = map.get(name);

        // doesn't exist yet - create and store
        if(lock == null) {
            lock = new Lock();
            map.put(name, lock);
        }
    }

    lock.lock();
}

LockManager.releaseLock(String name) {
    // unlock
    // if this was the last hold on the lock, remove it from the cache
}

答案 1

我看到的所有这些答案都太复杂了。为什么不直接使用:

public void executeInNamedLock(String lockName, Runnable runnable) {
  synchronized(lockName.intern()) {
    runnable.run();
  }
}

关键点是方法 :它确保返回的 String 是全局唯一对象,因此它可以用作 vm 实例范围的互斥体。所有被拘留的字符串都保存在全局池中,因此这就是您在原始问题中谈论的静态缓存。不要担心咎咧;如果没有其他线程引用这些字符串,则这些字符串将被gc'ed。但请注意,在 Java6 之前(包括 Java6),此池保留在 PermGen 空间中,而不是堆中,因此您可能需要增加它。intern

但是,如果 vm 中的其他一些代码出于完全不同的原因锁定在同一个字符串上,则存在问题,但是 a) 这不太可能,b) 您可以通过引入命名空间来绕过它,例如executeInNamedLock(this.getClass().getName() + "_" + myLockName);


答案 2

你能有一个?每次你需要一把锁,你基本上都会打电话。Map<String, java.util.concurrent.Lock>map.get(lockName).lock()

下面是一个使用谷歌番石榴的例子

Map<String, Lock> lockMap = new MapMaker().makeComputingMap(new Function<String, Lock>() {
  @Override public Lock apply(String input) {
    return new ReentrantLock();
  }
});

然后,如果需要,将导致创建一个新锁并返回给您。然后,您可以调用该锁。 返回一个线程安全的 Map,因此您可以与所有线程共享该映射。lockMap.get("anyOldString")lock()makeComputingMap


推荐