8

以前はHashMapを使っていました

   public Map<SocketChannel, UserProfile> clients = new HashMap<SocketChannel, UserProfile>();

同期ブロックを回避するためにConcurrentHashMapに切り替えましたが、サーバーに毎秒200〜400の同時クライアントが大量にロードされ、時間の経過とともに大きくなることが予想されるという問題が発生しています。

これは今このように見えます

public ConcurrentHashMap<SocketChannel, UserProfile> clients = new ConcurrentHashMap<SocketChannel, UserProfile>();

私のサーバー設計はこのように機能します。大量のパケットを処理するためのワーカースレッドがあります。各パケットは、packetHandlerサブルーチン(スレッドの一部ではない)でチェックされます。ほとんどすべてのクライアントが、静的とほぼ同じようにいつでも呼び出すことができますが、そうではありません。

私のサーバー全体は、パケット処理部分を除いて、ほとんどシングルスレッドです。

とにかく、誰かがオンラインですべてのクライアントを数え、それらからいくつかの情報を取得するようなコマンドを使用するとき。

カウントの進行中にクライアントが切断されてConcurrentHashMapから削除される可能性もあります(これが私の問題の原因です)。

また、ここにいくつかのコードを追加したいと思います。

                int txtGirls=0;
                int vidGirls=0;
                int txtBoys=0;
                int vidBoys=0;
                Iterator i = clients.values().iterator();
                while (i.hasNext()) {
                    UserProfile person = (UserProfile)i.next();
                    if(person != null) {
                        if(person.getChatType()) {
                            if(person.getGender().equals("m"))
                                vidBoys++;
                            else //<-- crash occurs here.
                                vidGirls++;
                        } else if(!person.getChatType()) {
                            if(person.getGender().equals("m"))
                                txtBoys++;
                            else
                                txtGirls++;
                        }
                    }
                }

もちろん、イテレータ内にtry-catch例外を追加して、これらのnullクライアントをスキップすることで修正します。

しかし、上記のif(person!= null)をチェックしても、ネストされたコードが自動的に機能しないかどうかはわかりません。

それが反復中に削除されたことを意味しない場合、それはスレッドセーフなwtfであるため、不可能であるはずです。

私は何をすべきか?または、try-catch Exceptionが最善の方法ですか?

これが例外です

java.lang.NullPointerException
    at Server.processPackets(Server.java:398)
    at PacketWorker.run(PacketWorker.java:43)
    at java.lang.Thread.run(Thread.java:636)

processPacketsには、上記のコードが含まれています。コメントは行数を示します#

私を啓発してくれてありがとう。

4

4 に答える 4

16

コレクションConcurrentHashMap.values()の反復子がどのように機能するかについての次の説明に特に注意して、メソッドの javadoc を読む必要があります。values()

「ビューの反復子は、ConcurrentModificationException をスローすることのない「弱い一貫性のある」反復子であり、反復子の構築時に存在していた要素をトラバースすることを保証し、構築後の変更を反映する可能性があります (ただし、保証されていません)。」

イテレータは、値のコレクションの状態の一貫したスナップショットを提供しませんが、スレッドセーフであり、予期される動作の範囲が明確に指定されています

マップ内の値 (またはキー、またはエントリ) の一貫したスナップショットを提供し、変更と同時に反復できる Map 実装が必要な場合は、おそらくカスタム Map ラッパー クラス (コレクションをコピーする) を作成する必要があります。アトミックに) ... または本格的なカスタム Map 実装。どちらも、ユースケースの ConcurrentHashMap よりもはるかに遅くなる可能性があります。

于 2010-09-16T01:12:07.827 に答える
3

java.util.concurrent.ConcurrentHashMapはnull値を許可しません。したがって、コード内のnull check(person!= null)は不要です。

反復中にマップの変更を拒否する場合は、上記のコードとすべての変更操作コードで同期ブロックを使用する必要があります。

于 2010-09-16T01:21:42.410 に答える
1

コードに問題はありません。でクラッシュが実際に発生する可能性は低いため、メソッドがを返しelseている可能性があります。getGender()null

于 2010-09-16T01:16:01.427 に答える
1

反復処理中にマップを変更できない場合があります。その場合は、別のコレクションで値とキーを取得し、それを反復処理することをお勧めします。これは不変であるためです。

完璧ではありませんが、他のオプションは拡張することConcurrentHashMapであり、何かが追加または削除されたときにこれらの 4 つの変数を更新するため、毎回リスト全体を反復処理する必要はありません。 CPUサイクル。

役に立つかもしれないいくつかのリンクを次に示します。

これは、改善された並行性がいくつかの約束の緩和によるものであるという事実について少し話しています。 http://www.ibm.com/developerworks/java/library/j-jtp07233.html

メモリ一貫性プロパティの説明: http://download-llnw.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility

于 2010-09-16T01:11:23.093 に答える