【发布时间】:2020-09-09 14:37:56
【问题描述】:
我想实现基于用户的锁定,如下代码所示。
如果在用户“1234”(java.lang.String.class 标识符)上发生了某个进程,或者某个进程正在使用用户,则其他进程应该等待该进程完成。听起来很简单,我尝试使用ReenterrentLock 使其工作,但我陷入了永久等待状态和死锁,虽然我正准备使用synchronised 块使其工作,但我想用ReenterrentLock 实现这一目标。
以下是我的代码和日志。
让我知道我做错了什么。
//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();
}
}
}
/**
* AccountLock
*/
final class AccountLock {
private static final Map<String, Lock> LOCK_MAP = Collections.synchronizedMap(new HashMap<>());
private static volatile AccountLock INSTANCE;
private AccountLock() {
}
public static AccountLock getInstance() {
if (INSTANCE == null) {
synchronized (AccountLock.class) {
if (INSTANCE == null) {
INSTANCE = new AccountLock();
}
}
}
return INSTANCE;
}
public void lock(String user) {
LOCK_MAP.computeIfPresent(user, (s, lock) -> {
lock.lock();
return lock;
});
LOCK_MAP.computeIfAbsent(user, s -> {
final ReentrantLock lock = new ReentrantLock(true);
lock.lock();
return lock;
});
}
public void unlock(String user) {
LOCK_MAP.computeIfPresent(user, (s, lock) -> {
lock.unlock();
return null;
});
}
}
//logs
//waiting tag=2process,user=1234,TH=pool-1-thread-2
//waiting tag=3process,user=5678,TH=pool-1-thread-3
//waiting tag=1process,user=5678,TH=pool-1-thread-1
//waiting tag=4process,user=1234,TH=pool-1-thread-4
//waiting tag=5process,user=5678,TH=pool-1-thread-5
//in tag=3process,user=5678,TH=pool-1-thread-3
//in tag=4process,user=1234,TH=pool-1-thread-4
//in tag=5process,user=5678,TH=pool-1-thread-5
//waiting tag=6process,user=1234,TH=pool-1-thread-6
//waiting tag=7process,user=5678,TH=pool-1-thread-7
//waiting tag=8process,user=1234,TH=pool-1-thread-8
如果您需要相同的线程转储,请告诉我。
我在 mac 上使用 java8
【问题讨论】:
-
看起来当你第一次尝试锁定一个用户时,你只是实例化了一个锁,你没有锁定它。 (第一次:空地图。
computeIfPresent因此什么都不做,computeIfAbsent创建锁,但不接受它)。否则,所罗门可能是对的(尽管我猜sleep是用于演示目的)。而且你也有一些无限的内存使用。 -
关于这个模式的一些讨论:stackoverflow.com/questions/5639870/…
-
@GPI 谢谢!链接非常有帮助,一些答案也提出了同样的方法,你能帮我找出这里的死锁吗?我已经更新了一些代码
标签: java concurrency reentrantlock