首页 > 解决方案 > 正确使用 ConcurrentLinkedDeque

问题描述

你好,与使用linkedlist with (reentrant lock)或使用相反,ArrayList (with Collections.synchronizedList)我读到使用ConcurrentLinkedDeque将消除使用读/写锁。我要做的是添加一个游戏角色对象,每次角色进入地图(字段)或离开地图时,都会添加和删除该对象。我们可以让数百名玩家同时进入或离开任何地图......

这就是我所做的

private final ConcurrentLinkedDeque<User> charUsers;

public Field() {
    this.charUsers = new ConcurrentLinkedDeque<>();
}

public User getUserByID(int id) {
    return charUsers.stream().filter(c -> c.getId() == id).findFirst().orElse(null);
}

public void addUser(User chr) {
    if (!charUsers.contains(chr)) {
        charUsers.add(chr);
    }
    broadcastPacket(UserPool.userEnterField(chr), chr);
}

public void removeUser(User chr) {
    charUsers.remove(chr); // or charUsers.remove();?
    broadcastPacket(UserPool.userLeaveField(chr), chr);
}

public void broadcastPacket(byte[] outPacket, User exceptChr) {
    charUsers.stream().filter(chr -> !chr.equals(exceptChr)).forEach(
            chr -> chr.getClient().getSession().send(outPacket)
    );
}

我有点担心并发,我读过它ConcurrentLinkedDeque在多线程环境中是线程安全的,但是在使用迭代器时它的一致性很弱。我试图避免任何ConcurrentModificationExceptions.

如果不是这种情况,我会更好地使用 sync.List with ArrayListor aLinkedList吗?

与此游戏设计相关的一个附带问题,我认为这是基于一个相当古老的基于 Java 的游戏,当时我相信它使用 Java 1.5,而他们用来hashmaps为游戏添加/删除实体/对象(字符、地图、小怪,NPC)。我相信即使使用ReentrantLock. 所以我正在尝试一种不同的方法。(虽然这个问题可能还有其他因素,但我认为这是主要因素)。

标签: java

解决方案


因此,从您发布的代码看来,您正在使用charUsers的只是跟踪当前地图中的所有内容。您正在扫描集合以查找实体,并且您正在添加和删除实体。而且您担心对集合的并发访问?一致性怎么样,你担心吗?就像,线程 A 添加了一个东西,线程 B 删除了它,然后线程 A 试图表现得好像该东西仍在集合中一样;你关心?这是您的并发问题的一部分。

现在,如果您想要的是一个列表,并且您将一直添加和删除节点,而不必在末尾添加和删除节点,那么链表就很棒。但是,如果您想在集合中搜索特定项目并且您通常不关心所有项目,那么链接列表会很糟糕。毕竟,也许您想要一个地图—— ConcurrentHashMap,其中键是事物的 ID,值是事物。每次你搜索一个东西时,你都必须遍历那个列表直到找到它,这最终是一个 O(N) 问题,对,当你遍历那个列表时,你会面临并发修改(也许你想要一个 CopyOnWriteArrayList,如果你有 RAM 要刻录的话)。

我同意另一位评论者的观点,即这个问题可能过于宽泛,无法得到一个可靠的是/否答案。有许多不同的方法可以最小化同步和并发访问的成本,但它们都是由系统的目标驱动的。一种方法可能对一个系统很好,但对另一个系统很糟糕。

我建议做一些幼稚且可行的事情并对其进行分析,或者以其他方式根据您期望的结果来衡量其性能。这可以让你看到你真正需要优化的地方。


推荐阅读