I want to achieve user based locking as shown in below code.
If on user "1234" (java.lang.String.class identifier) some process is happening, or user is being used by some process, other processes should wait for that process to complete. Simple as that it sounds, I tried making it work using ReenterrentLock
but I get stuck in perpetual waiting state and deadlock, though I was about to make it work using synchronised
block, I wanted to achieve this with ReenterrentLock
.
Below is my code & logs.
Let me know what I am doing wrong.
//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
Let me know if you need thread dumps for the same.
I am using java8 on mac