哪个更有效:原子修改数组或同步访问它?

时间:2013-01-08 14:47:52

标签: java performance synchronization atomic

考虑这两个代码示例:

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)将自己从玩家列表中删除。由于这些操作是从不同的线程调用的,因此绝对需要同步。 那我该使用哪个?

3 个答案:

答案 0 :(得分:2)

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

  

每次他们完成连接和登录后,他们都会添加   他们自己到玩家列表,通过addPlayer(播放器)

通过网络连接/登录等的成本将大大使您关注的任何效率相形见绌。

这个断言:

  

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

过去更受关注。如今的同步成本要低得多。

对于上述内容,我真的不担心。让您的解决方案正常工作,然后确定上述内容是否足够低效以保证重新处理。我在上面的表现中看不到与我有关的事情。

答案 1 :(得分:1)

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

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

答案 2 :(得分: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;
    }
}

因为您将无法以安全的方式使用返回的数组。鉴于您只访问最终字段,synchronized将不会执行任何操作。