答案 0 :(得分:3)
您可能需要查看同步(集合)的工作方式:
这(作为非排他性示例)不是线程安全的:
if (users.get(userA).size() > 1) {
users.get(userA).remove(0)
请记住,只有个人“synchronized”方法在没有更大锁定范围的情况下才能保证原子性。
快乐的编码。
编辑 - 按用户同步锁定(已更新以供评论):
通过使用标准数据结构,您可以使用ConcurrentHashMap实现每个键锁定 - 特别是使用'putIfAbsent'方法。 (这与明显不同比仅使用'同步HashMap'的get / put,见上文。)
下面是一些伪代码和注释:
public boolean add(long userA, long userB, String action) {
// The put-if-absent ensures the *the same* object but may be violated when:
// -users is re-assigned
// -following approach is violated
// A new list is created if needed and the current list is returned if
// it already exists (as per the method name).
// Since we have synchronized manually here, these lists
// themselves do not need to be synchronized, provided:
// Access should consistently be protected across the "higher"
// structure (per user-entry in the map) when using this approach.
List listA = users.putIfAbsent(userA, new List)
List listB = users.putIfAbsent(userB, new List)
// The locks must be ordered consistently so that
// a A B/B A deadlock does not occur.
Object lock1, lock2
if (userA < userB) {
lock1 = listA, lock2 = listB
} else {
lock1 = listB, lock2 = listA
}
synchronized (lock1) { synchronized (lock2) {{ // start locks
// The rest of the code can be simplified, since the
// list items are already *guaranteed* to exist there is no
// need to alternate between add and creating a new list.
bool eitherUserBusy = listA.length > 0 || listB.length > 0
listA.add(action)
listB.add(action)
// make sure messages allows thread-safe access as well
messages.put(action, [userA, userB])
return !eitherUserBusy
}} // end locks
}
在您的使用场景下,我没有关于单个常见锁定对象的展示方式。除非有明显的优势,否则通常建议采用“更简单”。
HTH和Happy编码。
答案 1 :(得分:2)
答案 2 :(得分:1)
在类中有两个全局状态持有者,并且在两个方法中的每个方法中都有复合操作来修改它们。因此,即使我们将Map的ConcurrentHashMap和List更改为CopyOnWriteArrayList之类的东西,它仍然不能保证一致的状态。
我看到你经常会写入List,所以,CopyOnWriteArrayList反正可能太贵了。 ConcurrentHashMap只有16路条带。如果你有更好的硬件,另一种选择是Cliff Click的highscalelib(在方法中适当锁定之后)。
回到一致性问题,如何使用ReentrantLock而不是同步,看看是否可以从lock() - to-unlock()序列中排除一些语句。如果您使用ConcurrentMap,add()中包含KeyK的前两个语句可以是乐观的,您可以将它们从锁定块中排除。
你真的需要消息地图吗?它有点像用户的反向索引。另一个选项是使用另一个watch()方法,在更改用户后,根据add()中的信号定期更新消息映射。或者,刷新可以完全是异步的。在这样做时,您可以在更新消息时对用户使用ReadWockLock和readLock()。在这种情况下,add()可以安全地获取用户的writeLock()。要使这个合理正确,还有一些工作要做。