luk*_*m00 26 java concurrency multithreading thread-safety
我最近一直在阅读Java Concurrency in Practice - 很棒的书.如果你认为你知道并发是如何工作的,但是大部分时间你都面对真正的问题,感觉SWAG是你能做的最多,那么本书肯定会对这个话题有所了解.当你尝试在线程之间共享数据时,有多少东西实际上会出错,这有点可怕.我想这让我对线程安全感觉有点疯狂.现在我担心的是,由于同步太多,我可能会遇到一些活动问题.这是一段代码来说明:
private final Hashtable<String, AtomicInteger> userSessions =
new Hashtable<String, AtomicInteger>();
public void registerUser(String userLogin) {
synchronized(userSessions) {
AtomicInteger sessionCount = userSessions.get(userLogin);
if (sessionCount != null) {
sessionCount.incrementAndGet();
} else {
userSessions.put(userLogin, new AtomicInteger(1));
}
}
}
public void unregisterUser(String userLogin) {
synchronized(userSessions) {
AtomicInteger sessionCount = userSessions.get(userLogin);
if (sessionCount != null) {
sessionCount.decrementAndGet();
}
}
}
public boolean isUserRegistered(String userLogin) {
synchronized(userSessions) {
AtomicInteger sessionCount = userSessions.get(userLogin);
if (sessionCount == null) {
return false;
}
return sessionCount.intValue() > 0;
}
}
Run Code Online (Sandbox Code Playgroud)
我试着把它弄好:同步集合在静态部分构造并存储在静态最终引用中以便安全发布,锁定集合(而不是this- 因此我不会阻塞代码所在的整个类)并使用原子原语的包装类.这本书提到过度使用这也可能会引起问题,但似乎我需要更多的时间来完全包裹它.你如何使这个代码线程安全,并确保它不会受到活动和性能问题的影响?
编辑:把它变成实例方法和变量,原来一切都被声明为静态 - 糟糕,糟糕的设计.也使userSessions私有(不知何故我以前把它公之于众).
Tom*_*ine 14
使用一个ConcurrentHashMap,你可以使用putIfAbsent.您无需AtomicInteger编码即可进行同步.
public final ConcurrentMap<String, AtomicInteger> userSessions =
new ConcurrentHashMap<String, AtomicInteger>();
public void registerUser(String userLogin) {
AtomicInteger newCount = new AtomicInteger(1);
AtomicInteger oldCount = userSessions.putIfAbsent(userLogin, newCount);
if (oldCount != null) {
oldCount.incrementAndGet();
}
}
public void unregisterUser(String userLogin) {
AtomicInteger sessionCount = userSessions.get(userLogin);
if (sessionCount != null) {
sessionCount.decrementAndGet();
}
}
public boolean isUserRegistered(String userLogin) {
AtomicInteger sessionCount = userSessions.get(userLogin);
return sessionCount != null && sessionCount.intValue() > 0;
}
Run Code Online (Sandbox Code Playgroud)
注意,这个泄漏......
尝试非泄漏版本:
public final ConcurrentMap<String, Integer> userSessions =
new ConcurrentHashMap<String, Integer>();
public void registerUser(String userLogin) {
for (;;) {
Integer old = userSessions.get(userLogin);
if (userSessions.replace(userLogin, old, old==null ? 1 : (old+1)) {
break;
}
}
}
public void unregisterUser(String userLogin) {
for (;;) {
Integer old = userSessions.get(userLogin);
if (old == null) {
// Wasn't registered - nothing to do.
break;
} else if (old == 1) {
// Last one - attempt removal.
if (userSessions.remove(userLogin, old)) {
break;
}
} else {
// Many - attempt decrement.
if (userSessions.replace(userLogin, old, old-1) {
break;
}
}
}
}
public boolean isUserRegistered(String userLogin) {serLogin);
return userSessions.containsKey(userLogin);
}
Run Code Online (Sandbox Code Playgroud)
首先:不要使用Hashtable!它很旧,而且很慢.
附加:如果您已经在更高级别上同步,则不需要在较低级别上进行同步(对于AtomicInteger-thing也是如此).
根据这里需要什么用例,我在这里看到了不同的方法.
读/写方法
假设您isUserRegistered经常调用该方法而其他方法只是偶然调用,一种好的方法是读写锁:允许同时进行多次读取,但只有一个写锁来统治它们(只有获得其他锁定才能获得).
private static final Map<String, Integer> _userSessions =
new HashMap<String, Integer>();
private ReadWriteLock rwLock =
new ReentrantReadWriteLock(false); //true for fair locks
public static void registerUser(String userLogin) {
Lock write = rwLock.writeLock();
write.lock();
try {
Integer sessionCount = _userSessions.get(userLogin);
if (sessionCount != null) {
sessionCount = Integer.valueOf(sessionCount.inValue()+1);
} else {
sessionCount = Integer.valueOf(1)
}
_userSessions.put(userLogin, sessionCount);
} finally {
write.unlock();
}
}
public static void unregisterUser(String userLogin) {
Lock write = rwLock.writeLock();
write.lock();
try {
Integer sessionCount = _userSessions.get(userLogin);
if (sessionCount != null) {
sessionCount = Integer.valueOf(sessionCount.inValue()-1);
} else {
sessionCount = Integer.valueOf(0)
}
_userSessions.put(userLogin, sessionCount);
} finally {
write.unlock();
}
}
public static boolean isUserRegistered(String userLogin) {
boolean result;
Lock read = rwLock.readLock();
read.lock();
try {
Integer sessionCount = _userSessions.get(userLogin);
if (sessionCount != null) {
result = sessionCount.intValue()>0
} else {
result = false;
}
} finally {
read.unlock();
}
return false;
}
Run Code Online (Sandbox Code Playgroud)
Pro:简单易懂
Con:如果频繁调用write方法,则不会缩放
小原子操作方法
想法是做小步骤,这些步骤都是原子的.无论如何,这将带来非常好的表现,但这里有许多隐藏的陷阱.
public final ConcurrentMap<String, AtomicInteger> userSessions =
new ConcurrentHashMap<String, AtomicInteger>();
//There are other concurrent Maps for different use cases
public void registerUser(String userLogin) {
AtomicInteger count;
if (!userSession.containsKey(userLogin)){
AtomicInteger newCount = new AtomicInteger(0);
count = userSessions.putIfAbsent(userLogin, newCount);
if (count == null){
count=newCount;
}
//We need ifAbsent here, because another thread could have added it in the meantime
} else {
count = userSessions.get(userLogin);
}
count.incrementAndGet();
}
public void unregisterUser(String userLogin) {
AtomicInteger sessionCount = userSessions.get(userLogin);
if (sessionCount != null) {
sessionCount.decrementAndGet();
}
}
public boolean isUserRegistered(String userLogin) {
AtomicInteger sessionCount = userSessions.get(userLogin);
return sessionCount != null && sessionCount.intValue() > 0;
}
Run Code Online (Sandbox Code Playgroud)
Pro:非常好的缩放
Con:不直观,很快就会很复杂,并不总是可能,很多隐藏的陷阱
锁定每用户的方法
这将为不同的用户创建锁,假设有很多不同的用户.您可以使用一些小的原子操作创建锁或监视器,并锁定它们而不是完整列表.
对于这个小例子来说太过分了,但对于非常复杂的结构来说,这可能是一个优雅的解决方案.
从您的代码来看,您的同步_userSessions应该足够了,因为您没有公开AtomicInteger对象。
AtomicInteger在这种情况下,不需要提供的附加安全性,因此本质上您在这里将其用作可变的Integer。如果您担心额外的开销,您可以放置一个包含会话计数的嵌套静态类作为映射中的唯一属性AtomicInteger(或者有点难看:将 int[1] 添加到映射中,只要它们不暴露)本课程之外。)
| 归档时间: |
|
| 查看次数: |
2000 次 |
| 最近记录: |