I am using ConcurrentSkipListMap in my code and I want to ensure a method to be concurrent-safe.
public synchronized UserLogin getOrCreateLogin(Integer userId) {
// new user
if (!sessionCache.containsKey(userId)) {
String sessionKey = sessionKeyPool.getKey();
sessionCache.putIfAbsent(userId, sessionKey);
return userSessions.computeIfAbsent(sessionKey, userLogin -> new UserLogin(userId, sessionKey));
} else { // old user, check session
String oldSessionKey = sessionCache.get(userId);
UserLogin old = userSessions.get(oldSessionKey);
if (!old.isExpired()) return old;
log.info("Session expired! Create another. User id: " + userId + ", last login: " + old.getStartTime());
String newKey = sessionKeyPool.getKey();
UserLogin another = new UserLogin(userId, newKey);
userSessions.replace(newKey, another);
sessionCache.replace(userId, newKey);
return another;
}
}
I know that the map atomatically does containsKey()
, get
and so one. But does not mean the whole block cannot be accessed by two threads at the same time, but at least between line and line the happen-before relationship is guarded(the first thread reaches this line will exit this line first). But, then how to actually use these methods to have a composable and consistent result? How do I lock these lines?
How can I lock to gain the finest granularity without compromising the consistency of output data?