1

次の2つのコードサンプルを検討してください。

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

オプション1のみがスレッドセーフです。

player[i]オプション2は、スレッドセーフではない方法でnullかどうかをチェックします。

于 2013-01-08T14:54:38.887 に答える
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は何もしません。

于 2013-01-08T15:09:51.183 に答える