MySQL有一个方便的功能:
SELECT GET_LOCK("SomeName")
Run Code Online (Sandbox Code Playgroud)
这可用于为应用程序创建简单但非常具体的基于名称的锁.但是,它需要数据库连接.
我有很多情况,比如:
someMethod() {
// do stuff to user A for their data for feature X
}
Run Code Online (Sandbox Code Playgroud)
它没有意义的只是同步的这种方法,因为,例如,如果调用此方法对于在此期间用户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')")
}
Run Code Online (Sandbox Code Playgroud)
由于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
}
Run Code Online (Sandbox Code Playgroud)
dmo*_*ius 33
我看到的所有答案都太复杂了.为什么不简单地使用:
public void executeInNamedLock(String lockName, Runnable runnable) {
synchronized(lockName.intern()) {
runnable.run();
}
}
Run Code Online (Sandbox Code Playgroud)
关键点是方法intern:它确保返回的String是全局唯一对象,因此它可以用作vm实例范围的互斥锁.所有实习字符串都保存在一个全局池中,因此这是您在原始问题中讨论的静态缓存.不要担心memleaks; 如果没有其他线程引用它,那些字符串将被gc'ed.但是请注意,直到并包括Java6此池都保存在PermGen空间而不是堆中,因此您可能需要增加它.
但是如果你的vm中的某些其他代码由于完全不同的原因锁定在同一个字符串上会有问题,但是a)这是不太可能的,并且b)你可以通过引入名称空间来绕过它,例如 executeInNamedLock(this.getClass().getName() + "_" + myLockName);
sjr*_*sjr 21
你有Map<String, java.util.concurrent.Lock>吗?每次你需要一个锁,你基本上都会打电话map.get(lockName).lock().
以下是使用Google Guava的示例:
Map<String, Lock> lockMap = new MapMaker().makeComputingMap(new Function<String, Lock>() {
@Override public Lock apply(String input) {
return new ReentrantLock();
}
});
Run Code Online (Sandbox Code Playgroud)
然后,如果需要,lockMap.get("anyOldString")将导致创建一个新锁并返回给您.然后你可以调用那个锁.返回一个线程安全的Map,因此您可以与所有线程共享.lock()makeComputingMap
irr*_*ble 20
// pool of names that are being locked
HashSet<String> pool = new HashSet<String>();
lock(name)
synchronized(pool)
while(pool.contains(name)) // already being locked
pool.wait(); // wait for release
pool.add(name); // I lock it
unlock(name)
synchronized(pool)
pool.remove(name);
pool.notifyAll();
Run Code Online (Sandbox Code Playgroud)
moj*_*moj 15
也许这对你有用:jkeylockmanager
编辑:
我最初的反应可能有点短.我是作者,多次遇到这个问题,找不到现有的解决方案.这就是我在Google Code上创建这个小型库的原因.
也许稍晚但你可以使用Google Guava Striped
从概念上讲,锁定条带化是将锁分为多个条带的技术,增加了单个锁的粒度,允许独立操作锁定不同的条带并同时进行,而不是为单个锁创建争用.
//init
stripes=Striped.lazyWeakLock(size);
//or
stripes=Striped.lock(size);
//...
Lock lock=stripes.get(object);
Run Code Online (Sandbox Code Playgroud)
对于像用户名这样的东西的锁定,Lock地图中的内存中的s可能有点漏洞.作为替代方案,您可以使用WeakReference与WeakHashMap一起创建互斥对象,当没有任何引用它们时可以对其进行垃圾回收.这可以避免您必须进行任何手动引用计数以释放内存.
你可以在这里找到一个实现.请注意,如果您在地图上频繁查找,则可能会遇到获取互斥锁的争用问题.
使用java.util.concurrent的通用解决方案
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
public class LockByName<L> {
ConcurrentHashMap<String, L> mapStringLock;
public LockByName(){
mapStringLock = new ConcurrentHashMap<String, L>();
}
public LockByName(ConcurrentHashMap<String, L> mapStringLock){
this.mapStringLock = mapStringLock;
}
@SuppressWarnings("unchecked")
public L getLock(String key) {
L initValue = (L) createIntanceLock();
L lock = mapStringLock.putIfAbsent(key, initValue);
if (lock == null) {
lock = initValue;
}
return lock;
}
protected Object createIntanceLock() {
return new ReentrantLock();
}
public static void main(String[] args) {
LockByName<ReentrantLock> reentrantLocker = new LockByName<ReentrantLock>();
ReentrantLock reentrantLock1 = reentrantLocker.getLock("pepe");
try {
reentrantLock1.lock();
//DO WORK
}finally{
reentrantLock1.unlock();
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
28012 次 |
| 最近记录: |