Java 使用synchronizedMap的用户级锁定方法&;重入锁
我想实现如下代码所示的基于用户的锁定 如果在用户“1234”(java.lang.String.class identifier)上发生了某个进程,或者某个进程正在使用该用户,则其他进程应等待该进程完成。听起来很简单,我尝试使用Java 使用synchronizedMap的用户级锁定方法&;重入锁,java,concurrency,reentrantlock,Java,Concurrency,Reentrantlock,我想实现如下代码所示的基于用户的锁定 如果在用户“1234”(java.lang.String.class identifier)上发生了某个进程,或者某个进程正在使用该用户,则其他进程应等待该进程完成。听起来很简单,我尝试使用reeterrentlock使其工作,但我陷入了永久等待状态和死锁,尽管我即将使用synchronized块使其工作,但我想通过reeterrentlock实现这一点 下面是我的代码和日志 让我知道我做错了什么 //Research.java file public
reeterrentlock
使其工作,但我陷入了永久等待状态和死锁,尽管我即将使用synchronized
块使其工作,但我想通过reeterrentlock
实现这一点
下面是我的代码和日志
让我知道我做错了什么
//Research.java file
public static final int PERIOD = 10;
public static final int END_INCLUSIVE = 23;
public static final int N_THREADS = 8;
public static void main(String[] args) {
final ExecutorService executorService = Executors.newFixedThreadPool(N_THREADS);
IntStream.rangeClosed(1, END_INCLUSIVE).forEach(value -> executorService.submit(() -> {
final String user = value % 2 == 0 ? "1234" : "5678";
process(value + "process", user);
}));
executorService.shutdown();
}
private static void process(String tag, String user) {
System.out.println("waiting tag=" + tag + ",user=" + user + ",TH=" + Thread.currentThread().getName());
AccountLock.getInstance().lock(user);
System.out.println("in tag=" + tag + ",user=" + user + ",TH=" + Thread.currentThread().getName());
sleep(tag, PERIOD);
AccountLock.getInstance().unlock(user);
System.out.println("out tag=" + tag + ",user=" + user + ",TH=" + Thread.currentThread().getName());
}
private static void sleep(String tag, long s) {
boolean interrupt = false;
try {
TimeUnit.SECONDS.sleep(s);
} catch (InterruptedException e) {
interrupt = true;
e.printStackTrace();
} finally {
if (interrupt) {
Thread.currentThread().interrupt();
}
}
}
如果您需要相同的线程转储,请告诉我
我在mac上使用java8死锁是由地图和锁的交互产生的 更准确地说:您使用的是
Collections.syncrhonized
map,这实际上是对原始map实例的每个(大约)方法进行互斥
这意味着,例如,只要有人在computeifassent
(或computeIfPresent
)调用中,就不能在map实例上调用其他方法
到目前为止没有问题
除了,在computeIfxx
调用中,您可以通过获取锁实例来执行另一个锁操作
拥有两个不同的锁,而不保持严格的锁获取顺序,是造成死锁的最佳方法,您在这里演示了这一点
年表样本:
map.computeXX
,释放M.T1保存L1(仅限)lock(String user) {
LOCKS.computeIfAbsent(user, u -> new ReentrantLock()).lock();
}
通过将锁获取移到map的方法调用之外,我将其从map的互斥体中移除,并消除了死锁的可能性
修正后,我创建了另一个bug
在解锁端,使用返回null的computeIfPresent
。这意味着一旦锁定完成,您将打算将其从地图中移除。这一切都很好,但它在解锁情况下创建了竞争条件
public void unlock(String user) {
LOCK_MAP.computeIfPresent(user, (s, lock) -> {
lock.unlock();
return lock;
});
但是没有人会从地图上释放密钥,而地图会无限期地增长——可以添加一个线程安全计数器,在释放之前检查锁是否“被获取”。这会不会反过来产生另一个bug
话虽如此,这里仍有一些需要加强的地方:
- 锁释放应在最终块中执行
- 您创建的锁实例数量是无限的。您可能希望避免这种情况(这是真正困难的部分)
- 在地图级别使用互斥是一种浪费。ConcurrentHashMap将提供更细粒度的锁机制
- 谁知道这里还剩下什么虫子
并发编程很难。其他人已经为您这样做了。看起来,当您第一次尝试锁定用户时,您只实例化了一个锁,而没有锁定它。(第一次:空映射。
computeIfPresent
因此不执行任何操作,computeifapsent
创建锁,但不接受锁)。否则,Solomon可能是对的(尽管我猜睡眠
是为了演示目的)。您也将面临一些无限制的内存使用。有关此模式的一些讨论:@GPI谢谢!链接非常有帮助,一些答案也有相同的建议,你能帮我识别这里的死锁吗?我已经更新了一点代码,非常感谢你们提供了详细的答案,我能够在这篇工作帖子上读到你们的答案。到目前为止还在工作,但我已经使用条纹锁将代码迁移到guava的条纹锁
lock(String user) {
LOCKS.computeIfAbsent(user, u -> new ReentrantLock()).lock();
}
public void unlock(String user) {
LOCK_MAP.computeIfPresent(user, (s, lock) -> {
lock.unlock();
return lock;
});