2 回答
TA贡献1821条经验 获得超4个赞
概述
好问题!重申上述评论中所述的内容,您正在寻找服务器端关闭。有一些方法可以处理这种情况,我可以用一个简短的例子来解释它。
执行服务器
我将根据这个例子运行一个修改过的例子。下面找到服务器实现。
class NetworkService implements Runnable {
private final ServerSocket serverSocket;
private final ExecutorService pool;
private final AtomicBoolean shouldExit;
public NetworkService(int port, int poolSize) throws IOException {
serverSocket = new ServerSocket(port);
pool = Executors.newFixedThreadPool(poolSize);
shouldExit = new AtomicBoolean(false); // Thread-safe boolean
}
public void run() { // run the service
try {
// While we should not exit
while(!shouldExit.get()) {
try {
pool.execute(new ClientHandler(serverSocket.accept()));
} catch (SocketException e) {
if(shouldExit.get()) break; // Poison pill has been delivered, lets stop
// Error handling
}
}
} catch (IOException ex) {
pool.shutdown();
}
// Clean up the thread pool
shutdownAndAwaitTermination();
}
}
class ClientHandler implements Runnable {
private final Socket socket;
ClientHandler (Socket socket) { this.socket = socket; }
public void run() {
...
}
...
}
在这里,您将修改当前的服务器代码以恐吓此结构。您目前有类似的妆容,但我们在这里添加了ExecutorService.
一个 Executor 提供管理终止的方法和可以生成 Future 以跟踪一个或多个异步任务的进度的方法。
通过将您的 ClientHandler 分派到ExecutorService,您正在使用ThreadPool. 虽然这带来了很多好处,但最重要的是您可以更好地控制多线程服务,ThreadPool管理线程利用率,并且应用程序效率将大大提高。
以下是您尝试关闭和终止所有剩余线程的方法:
void shutdownAndAwaitTermination(ExecutorService pool) {
pool.shutdown(); // Disable new tasks from being submitted
try {
// Wait a while for existing tasks to terminate
if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
pool.shutdownNow(); // Cancel currently executing tasks
// Wait a while for tasks to respond to being cancelled
if (!pool.awaitTermination(60, TimeUnit.SECONDS))
System.err.println("Pool did not terminate");
}
} catch (InterruptedException ie) {
// (Re-)Cancel if current thread also interrupted
pool.shutdownNow();
// Preserve interrupt status
Thread.currentThread().interrupt();
}
}
现在,问题仍然是我们如何关闭服务器?上面的代码显示了一个改进的结构,但仍然存在阻塞的问题serverSocket.accept()!
解决方案
想到这个场景时,有两个想法浮现在脑海中;CLI 或 GUI。两者具有相同的语义,最终由您决定。出于解释的目的,我将参考 CLI 方法。
毒丸
如果您实现new Thread()处理来自 CLI 的所有传入命令,则该线程将充当毒丸。这个想法是向目标递送毒丸,使其可以醒来/执行并死亡。该线程会将shouldExit原子布尔值更改为true并创建一个new Socket(serverSocket.getInetAddress(), serverSocket.getLocalPort()).close();以连接到ServerSocket并立即关闭它。在上面的代码中,应用程序将不再阻塞serverSocket.accept(). 相反,它将进入 try catchSocketExceptions并测试是否使用了毒丸;如果是,那么让我们清理,如果不是,让错误处理。
暂停
您还可以在 上设置超时,ServerSocket以便每次在该时间间隔内无法与myServer.setSoTimeout(2000);. 这将抛出一个InterruptedIOException并且 可以类似于毒丸处理,通过 CLI 命令更改标志并检查它是否应该在 catch 块中退出。如果它应该退出,让我们清理,否则让错误处理。
TA贡献1785条经验 获得超4个赞
您可以将模式标志与 volatile 布尔变量一起使用,并且您应该将它放在 'while' 中 - 当处理完成时,将其设置为 false 并且服务器将停止。
另一种方法 - 使用线程池并等待它们在服务器的主线程中完成。
添加回答
举报