2017-01-07 3 views
1

私は根本的な原因を理解できないという珍しい問題があるようです。JavaのServerSocketがスレッドをハングし、ブレークポイントを置くとロックが解除されますか?

私が書いているサーバーへの接続を処理するのにServerSocketを使用しています。 ServerSocketは独自のスレッドで接続を受け入れ、設定したisAcceptingとisActive変数を介してメインスレッドから制御できます。

何が起こるか: サーバーは接続を開始し、(パテ経由で)接続を受け入れています。私はコマンドを使用してサーバーソケットを閉じます。ソケットが閉じられ、スレッドがアイドル状態になります(これが原因でSocketExceptionが発生することがわかります)。私は、新しいサーバーソケットを開くためのコマンドを使用し、再度接続を受け付けます。私は接続することができるよとソケットをシャットダウンし、何が起こるループの接続を受け入れること

出てコマンドを経由してアプリケーションを終了することができます

サーバの起動をし、(パテ経由)接続を受け入れているが。私はコマンドを使用してサーバーソケットを閉じます。ソケットが閉じられ、スレッドがアイドル状態になります(これが原因でSocketExceptionが発生することがわかります)。私は新しいサーバーソケットを開くコマンドを使用し、それはスレッドがハングする場所です。コード内のデバッグ情報は表示されず、ServerSocketのオープン/クローズにも反応しません。 Exitコマンドを使用すると、exitルーチンでアプリケーションがハングアップします。面白いのは、スレッドコードのどこにでもブレークポイントを設定すると、そのスレッドは元に戻って終了し、終了します。コードを正常に実行するには、ブレークポイントを配置するまでソケットを閉じてスレッドを閉じます。

実行可能なJARにエクスポートしようとしましたが、Eclipseの場合と同様に、終了時にアプリケーションがハングします。以下のコードの

関連部品:私は、コードにブレークポイントを置くまで

public class ConnectionManager extends Thread implements IEverfreeManager { 

private final int defaultPort = 8002; 

private boolean isAccepting = true; 
private boolean isActive = true; 

private static ConnectionManager instance; 

private ServerSocket serverSocket; 
private int portNumber = defaultPort; 

private Socket workSocket; 

public static ConnectionManager instance(){ 
    if (instance == null) 
     instance = new ConnectionManager(); 
    return instance; 
} 

public ConnectionManager() { 

} 

public boolean isAccepting() { 
    return isAccepting; 
} 

public void setAccepting(boolean isAccepting) { 
    this.isAccepting = isAccepting; 
    try{ 
     if (!isAccepting && !serverSocket.isClosed()){    
      serverSocket.close(); 
      System.out.println("Closed server on port "+portNumber); 
     } else{ 
      serverSocket = new ServerSocket(portNumber); 
      System.out.println("Server on port "+portNumber+" is now accepting connections"); 
     } 
    }catch(Exception e){ 
     System.out.println("failed to stop accepting"); 
     e.printStackTrace(); 
    } 

} 

public boolean isActive() { 
    return isActive || isAlive(); 
} 

public void setActive(boolean isActive) { 
    this.setAccepting(isActive); 
    this.isActive = isActive; 

} 

public int getPortNumber() { 
    return portNumber; 
} 

public void setPortNumber(int portNumber) { 
    this.portNumber = portNumber; 
} 



private int getNewConnectionId(){ 
    return ++connectionIdCounter; 
} 

@Override 
public void run() { 
    super.run(); 


    try {  
      System.out.println("Starting up Connection Manager"); 
      System.out.println("Starting server on port "+portNumber); 
     serverSocket = new ServerSocket(portNumber); 
     System.out.println("Server running and ready to accept players"); 
     while (isActive){ 
      if (isAccepting){ 
       try{ 
        System.out.println("Waiting for connection..."); 
        workSocket = serverSocket.accept(); 
        System.out.println("Connected with "+workSocket.getInetAddress()); 
        int id = getNewConnectionId(); 

       } catch (SocketException e){ 
        System.out.println("Notice: "+e.getMessage()); 
       } 

      } 
     } 

    }catch(Exception e) { 

     e.printStackTrace(); 

    } 

} 

@Override 
public void closeManager() { 
    setActive(false);  
} 

はsetAccepting(false)を使用して、setAccepting(真)は

System.out.println("Waiting for connection..."); 

メッセージを生成しません。

setAccepting(false)の後にcloseManager()を使用すると、同じ結果が生成されます。ただcloseManagerを(使用

)setAcceptingに触れることなく()(シャットダウン時にアクティブ化手順を持つにもかかわらず)正常に終了し

任意の洞察力は非常に

答えて

0

をいただければ幸いですスレッドセーフは何も、このクラスではありません。ほとんどすべての機能には非常に根本的な問題があります。

isAcceptingとisActiveの両方がスレッドセーフであるように、どちらもvolatileであるか、または同期して変更する必要があります。別のスレッドがこれらのフィールドを変更する関数を呼び出していて、runメソッドがすでにループしていると、予期しない結果が生じる可能性があります。メモリの可視性の保証がないブール型フラグを表示しようとすると、常に悪い考えです。

setAccepting()は、run()スレッドが直ちに閉じようとしているソケットで待機しようとする競合状態にあります。

シングルトンConnectionManangerインスタンスに複数の値を作成させることができます。あなたの場合、コンストラクタは何もしませんが、インスタンスを作成する必要はありません。ダブルチェックロックを使用してこれを実装すると、1つのインスタンスしか作成されません。

あなたの即時の問題は、is *メンバーフィールドを両方ともvolatileにすることで '固定'になる可能性がありますが、このクラスではまだ多すぎる問題が多すぎてマルチスレッド環境で使用するのは完全ではないと言われました。さらに、Exceptionをキャッチして単純に印刷するのは通常間違っています。そして、通常は、実行可能ファイルをサブクラス化してスレッドのサブクラスを作成するのではなく、スレッドコンストラクタに渡したいとします。

+0

ありがとうございます!揮発性のキーワードがトリックでした。あなたのアドバイスに従ってクラスを修正しようとします –

関連する問題