1

2つのwhile条件があり、一方は他方の内側にあり、ブール値を使用してそれらを制御します。基本的に、1つは共有を終了するためのもので、もう1つは接続をリッスンするためのものです。ユーザーは共有を無効にすることを選択できます。この場合、サーバーはリッスンを停止しますが、終了しません。ユーザーが終了を選択した場合、両方のブール値がfalseに設定され、サイクルが終了します。

これは私のコードです:

        public void run() {
            while (!terminate) {  
                while (listening) {
                    try {
                        // accept connection -> create a new thread for each client
                        ClientServerShareInstance clientServerShareInstance = new ClientServerShareInstance(serverSocket.accept(), ui);
                        Thread clientServerThread = new Thread(clientServerShareInstance);
                        clientSockets.add(clientServerShareInstance);
                        connectedClients++;
                        clientServerThread.start();
                    } catch (IOException ex) {
                    }
                }
            }
        }

        public void closeAllClientConnections() {
            for (Iterator it = clientSockets.iterator(); it.hasNext();) {
                ClientServerShareInstance clientServerShareInstance = (ClientServerShareInstance) it.next();
                clientServerShareInstance.closeAllConnections();
                it.remove();
            }
            try {
                this.serverSocket.close();
            } catch (IOException ex) {}
            this.setActive(false);
            this.connectedClients = 0;
        }


        public void openConnection() {
            try {
                serverSocket = new ServerSocket(portNumber, 0, Inet4Address.getLocalHost());
                setActive(true);
            } catch (IOException ex) {}
        }
    }

このcloseAllClientConnections()メソッドは共有を無効にし(終了しません)、openConnection()その共有を再度有効にします。

問題は、共有を無効にすると、terminatewhile cicleを無期限にループして、の値をテストする必要があることですlistening。に設定listeningするとtrue、サーバーソケットを開くため、その2番目のwhileループに入り、リッスンを再開する必要があります(これとは関係ありませんが、次の場合に閉じるため、再度初期化する必要があると言っています。共有を無効にします)。ただし、無効にした後は、が呼び出されたlistening場合でも、ループをレンタルすることはありません。openConnection()

誰もがここで何が悪いのか知っていますか?

4

1 に答える 1

3

エラーを表示するのに十分なコードが表示されていません。しかし、ここに役立つかもしれないいくつかのコメントがあります。

  • shutdownとブール値の両方がでlisteningある必要がありますvolatile。スレッド間で共有されるフィールドは、何らかの方法で同期する必要があります。そうしないと、値の変更が他のスレッドに表示されません。

  • serverSocketvolatileの呼び出し元によって作成されたように見えますが、ループopenConnection()で消費されているため、これも必要になります。アクティブをtrueに設定するだけで、acceptスレッドによって完全に管理されているwhileと考えるかもしれません。openConnection()serverSocket

  • clientSocketsコレクションのようです。繰り返しになりますが、複数のスレッドによってアクセスされているように見えるため、これは同期接続である必要があります。繰り返しになりますが、より良いパターンは、closeAllClientConnections() 呼び出しがブール値を設定するだけで、スレッド自体が終了を行うことです。これにより、コレクションなどの使用に関する競合状態が解消されます。

  • あなたがそうであれば! terminating、あなたの受け入れスレッドが回転するように見えます。少なくともThread.sleep(100)、速度を落とすために何かを置く必要があります。待機/通知はさらに良いでしょう。

スレッド化されたプログラムで「同時に」何が起こるかだけではないことを理解することが重要です。また、メモリキャッシュについても説明します。受け入れスレッドはclientSockets ArrayList11分前に​​何かを追加した可能性があり、リストが何らかの理由で同期されていない場合、別のスレッドがそれらの変更を認識しない可能性があります。さらに悪いことに、の特定の部分はArrayListメモリ内で更新されている可能性がありますが、例外を引き起こす可能性のある他の部分は更新されていない可能性があります。同期されたコレクションを取得するには、次のArrayListようなものを作成する必要があります。

List<...> clientSockets = Collections.synchronizedList(new ArrayList<...>());

同期が必要な理由について、いくつかのドキュメントを読む必要があるようです。

http://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html

于 2012-06-08T13:05:24.163 に答える