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