【发布时间】:2016-02-12 00:32:55
【问题描述】:
为什么客户端代码没有终止? (在运行客户端之前,我们需要先启动服务端程序)
服务器代码:
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)
名称:信号调度员 状态:可运行 阻止总数: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 时生成的) 状态:在 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)
【问题讨论】:
-
“客户端代码不会终止”是什么意思?看来您的客户端甚至没有建立连接。
-
似乎从任何关闭挂钩调用 System.exit(status) 都会导致该线程和 DestroyJavaVM 线程之间的死锁。同样在 SIGINT 处理程序(如果按下 Control+C)线程和 DestroyJavaVM 线程之间。当然,在已经启动关机时调用 System.exit(status) 几乎没有意义。
-
嗯...非常有趣。你能打开你的任务管理器看看你的客户端产生了多少线程吗?
标签: java