套接字客户端不会终止

时间:2012-07-21 06:56:52

标签: java

为什么客户端代码没有终止? (我们需要在运行客户端之前先启动服务器程序)

服务器代码:

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主题:


名称:参考处理程序 状态:在java.lang.ref.Reference$Lock@14f38ff上等待 总被阻止:1总等待:2

堆栈追踪:

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

名称:终结者 状态:在java.lang.ref.ReferenceQueue$Lock@3037a0上等待 总被阻止: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$2@39b27b上等待 总被阻止: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 java.lang.Class@127bd04

名称:Thread-1(这是我在调用System.exit时添加的关闭钩子) 状态:由java.lang.Class@127bd04封锁: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处理程序(按下Control + C时似乎是spwaned) 状态:由java.lang.Class@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)

3 个答案:

答案 0 :(得分:0)

它不会停止,因为你有活动线程运行,(你的钩子)。或者你的意思是即使在向java进程发送SIGTERM或SIGINT信号之后你也无法阻止它?

答案 1 :(得分:0)

我不确定在这里使用addShutdownHook的意义。来自文档:

  

关机挂钩也应该快速完成工作。当程序调用exit时,期望虚拟机将立即关闭并退出。当虚拟机因用户注销或系统关闭而终止时,底层操作系统可能只允许一段固定的时间来关闭和退出。因此,不建议尝试任何用户交互或在关闭钩子中执行长时间运行的计算。

当然,终止时不需要socket.close(),因为如果您的代码没有机会关闭它,操作系统会自动为您执行此操作。我会尝试重构你的代码而不使用关闭钩子。

答案 2 :(得分:0)

您不需要在关闭钩子中调用System.exit(),并且您当然不应该这样做,因为它可能会产生死锁或无限递归。你也不需要其他的。最好使你的线程守护程序线程。