2012-07-21 121 views
0

爲什麼客戶端代碼不會終止? (我們需要在運行客戶端之前先啓動服務器程序)套接字客戶端不會終止

服務器代碼:

public class ServerSocketTest { 

    static final int PORT = 9999; 

    public static void main(String[] args) throws IOException { 
     final ServerSocket serverSocket = new ServerSocket(PORT); 

     Runtime.getRuntime().addShutdownHook(new Thread() { 
      @Override 
      public void run() { 
       try { 
        serverSocket.close(); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } 
      } 
     }); 

     while (true) { 
      final Socket socket = serverSocket.accept(); 
      Runnable runnable = new Runnable() { 

       @Override 
       public void run() { 
        System.out.println(socket); 
       } 
      }; 
      Thread thread = new Thread(runnable); 
      thread.start(); 
     } 

    } 

} 

客戶端代碼:

public class SocketClient { 

    public static void main(String[] args) throws UnknownHostException, 
      IOException { 
     final Socket socket = new Socket("localhost", ServerSocketTest.PORT); 

     Runtime.getRuntime().addShutdownHook(new Thread() { 
      @Override 
      public void run() { 
       System.out.println("waiting to close socket "); 
       printThread(Thread.currentThread()); 
       try {     
        socket.close(); 
       } catch (Exception e) { 
        e.printStackTrace(); 
       } 
      } 
     }); 

     Runtime.getRuntime().addShutdownHook(new Thread() { 
      @Override 
      public void run() { 
       System.out.println("waiting to exit"); 
       printThread(Thread.currentThread());     
       System.exit(0); 
      } 
     }); 
    } 

    static void printThread(Thread currentThread) { 
     System.out.println(currentThread + ": Alive=" + currentThread.isAlive() 
       + ",Daemon=" + currentThread.isDaemon()); 
    } 
} 

我使用Ubuntu。使用JConsole,我可以跟蹤以下線程加上其他一些RMI & JMX主題:


名稱:指向處理器 狀態:等待[email protected] 總阻塞:1總等待:2

堆棧跟蹤:

java.lang.Object.wait(Native Method) 
java.lang.Object.wait(Object.java:503) 
java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133) 

名稱:終結 統計E:對[email protected] 總等待阻止:1總等待:2

堆棧跟蹤:

java.lang.Object.wait(Native Method) 
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135) 
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151) 
java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:177) 

名稱:信號調度 國家:RUNNABLE 總阻塞:0總等待:0

堆棧跟蹤:


名稱:DestroyJavaVM 狀態:阻塞net.SocketClient[email protected] 總等待:0總等待:1個

堆棧跟蹤:

java.lang.Object.wait(Native Method) 
java.lang.Thread.join(Thread.java:1258) 
java.lang.Thread.join(Thread.java:1332) 
java.lang.ApplicationShutdownHooks.runHooks(ApplicationShutdownHooks.java:106) 
java.lang.ApplicationShutdownHooks$1.run(ApplicationShutdownHooks.java:46) 
java.lang.Shutdown.runHooks(Shutdown.java:123) 
java.lang.Shutdown.sequence(Shutdown.java:167) 
java.lang.Shutdown.shutdown(Shutdown.java:234) 
- locked [email protected] 

名稱:螺紋1(這是我添加的關閉鉤子調用System.exit) 狀態:BLOCKED on [email protected]歸屬:DestroyJavaVM 總阻塞:1等待總數:0

堆棧跟蹤:

java.lang.Shutdown.exit(Shutdown.java:212) 
java.lang.Runtime.exit(Runtime.java:107) 
java.lang.System.exit(System.java:960) 
net.SocketClient$2.run(SocketClient.java:31) 

名稱:SIGINT處理程序(這似乎當按下Ctrl + C組合被spwaned) 狀態:阻塞的java.lang。類@ 127bd04擁有者:DestroyJavaVM 總阻塞:1總等待:0

堆棧跟蹤:

java.lang.Shutdown.exit(Shutdown.java:212) 
java.lang.Terminator$1.handle(Terminator.java:52) 
sun.misc.Signal$1.run(Signal.java:212) 
java.lang.Thread.run(Thread.java:722) 
+0

你說的「客戶端代碼不會終止」是什麼意思?看來你的客戶甚至沒有建立連接。 – 2012-07-21 07:14:42

+0

似乎從任何關閉鉤子調用System.exit(status)都會導致該線程與DestroyJavaVM線程之間發生死鎖。在SIGINT處理程序(如果Control + C被按下)線程和DestroyJavaVM線程之間也是如此。當然,當關閉已經啓動時調用System.exit(status)幾乎沒有意義。 – 2012-07-21 13:29:56

+0

嗯......非常有趣。你能否請你打開你的任務管理器,看看你的客戶有多少線程? – 2012-07-21 17:51:08

回答

0

它不會停止,因爲你必須運行的活動線程的,(你的鉤子)。或者你的意思是說,即使在發送一個SIGTERM或SIGINT信號到java進程後,你也無法停止它?

+0

爲什麼鉤子不會終止,因爲它們沒有被分配任何非終止任務。即使Ctrl + C(SIGINT)也無法阻止它。我需要發出kill -9 – 2012-07-21 11:51:49

0

我不知道我在這裏看到使用addShutdownHook點。從文檔:

關閉掛鉤也應該快速完成他們的工作。當一個程序調用退出時,期望虛擬機會立即關閉並退出。當虛擬機由於用戶註銷或系統關閉而終止時,底層操作系統可能只允許關閉並退出的固定時間。因此,嘗試任何用戶交互或在關閉鉤子中執行長時間運行計算是不可取的。

當然,終止時無需socket.close(),因爲如果您的代碼沒有機會關閉它,OS會自動爲您執行該操作。我會嘗試重新編寫代碼,而不使用關閉掛鉤。

+0

什麼是由操作系統自動發佈的資源(如套接字)? – 2012-07-21 12:02:25

+0

打開文件,窗口句柄,套接字,管道(當其使用管道的所有進程被終止)等 – 2012-07-21 17:43:33

+0

感謝您的響應。請了解一些網站,其中記錄了這些資源的詳盡清單(以及特殊情況)。所有這些資源都必須是本地的,對嗎?操作系統可以追蹤由特定進程獲取的網絡資源(如遠程數據庫,MQ等)嗎? – 2012-07-22 05:40:23

0

你並不需要調用System.exit()在關閉掛鉤,你肯定不應該這樣做,因爲它可能會產生任何死鎖或無限循環。你也不需要其他的。讓你的線程守護線程更好。

+0

我已經達到了這樣的程度:「在關閉已經啓動時調用System.exit(status)幾乎沒有意義。」但是如果發生死鎖,共享資源是什麼?你可以肯定地告訴我或與我分享任何參考嗎?你是否認識到任何使關機掛鉤成爲守護進程的行爲改變? – 2012-07-22 05:46:09

+0

@labbhattacharjee對不起,我很失望,但這不是一個調試服務,我還沒有執行過你的代碼。而且我並不是建議讓你的關閉鉤子守護進程線程:它們是Runnables,所以使它們守護進程沒有影響。我建議徹底擺脫這些多餘的關閉掛鉤,並使剩下的線程守護進程。 – EJP 2012-07-23 03:12:09