1

考虑这两个代码示例:

private final Player[] players = new Player[MAX_PLAYERS + 1];
private int playerCount;

public boolean addPlayer(Player player) {
    synchronized (players) {
        for (int i = 1; i < players.length; i++) {
            if (players[i] == null) {
                players[i] = player;
                playerCount++;
                player.setIndex(i);
                return true;
            }
        }
        return false;
    }
}

public void removePlayer(Player player) {
    synchronized (players) {
        players[player.getIndex()] = null;
        playerCount--;
    }
}

public Player[] getPlayers() {
    synchronized (players) {
        return players;
    }
}

public int getPlayerCount() {
    synchronized (players) {
        return playerCount;
    }
}

和...

private final AtomicReferenceArray<Player> players = new AtomicReferenceArray<Player>(MAX_PLAYERS + 1);
private final AtomicInteger playerCount = new AtomicInteger();

public boolean addPlayer(Player player) {
    for (int i = 1; i < players.length(); i++) {
        if (players.get(i) == null) {
            players.set(i, player);
            playerCount.incrementAndGet();
            player.setIndex(i);
            return true;
        }
    }
    return false;
}

public void removePlayer(Player player) {
    players.set(player.getIndex(), null);
    playerCount.decrementAndGet();
}

public AtomicReferenceArray<Player> getPlayers() {
    return players;
}

public AtomicInteger getPlayerCount() {
    return playerCount;
}

现在,我知道通常访问数组是非常有效的。但是,我知道同步可能代价高昂。另一方面,原子操作不需要同步,但我猜这players.get(i)不如players[i]. 那么,如果我在游戏环境中使用这些样本中的哪一个会给我最好的性能呢?我设计了服务器,以便每个新玩家都有一个专用于他们的线程。每次他们完成连接和登录后,他们都会通过 将自己添加到玩家列表中addPlayer(Player)。当玩家断开连接时,他们会通过 将自己从玩家列表中删除removePlayer(Player)。由于这些操作是从不同的线程调用的,因此肯定需要同步。那么我应该使用哪个?

4

3 回答 3

2

这些关于 SO 的大多数问题都可以用“你测量过吗”来回答,并且可能非常依赖于你的环境。但是,无论您选择什么解决方案:

每次他们完成连接和登录时,他们都会通过 addPlayer(Player) 将自己添加到玩家列表中

通过网络连接/登录等的成本将使您担心的任何效率都相形见绌

这个断言:

但是,我知道同步可能代价高昂

以前比较担心。如今的同步成本大大降低。

对于上述情况,我真的不会担心。让您的解决方案发挥作用,然后确定上述是否足够低效以保证返工。我在上面看到的很少有性能相关引起我的关注。

于 2013-01-08T14:51:42.083 回答
1

这是最简单的写法

private final List<Player> players = new CopyOnWriteArrayList(); // thread safe

public boolean addPlayer(Player player) {
    return players.add(player);
}

public void removePlayer(Player player) {
    players.remove(player);
}

public List<Player> getPlayers() {
    return players;
}

public int getPlayerCount() {
    return players.size();
}

注意:这不起作用

public Player[] getPlayers() {
    synchronized (players) {
        return players;
    }
}

因为您将无法以安全的方式使用返回的数组。鉴于您只访问最终字段,同步不会做任何事情。

于 2013-01-08T15:09:51.183 回答
1

只有选项 1 是线程安全的。

选项 2player[i]以非线程安全的方式检查是否为 null。

于 2013-01-08T14:54:38.887 回答