【发布时间】:2022-01-24 20:50:37
【问题描述】:
我重构了一个单线程服务器,使其能够同时处理多线程并接受多个客户端。为此,我为每个新客户端生成一个新的ClientHandler 线程并将其提交给ExecutorService。我想通过在System.In 中输入新行来启动服务器关闭。
但是,我无法从内部关闭服务器(使用 Oracle 的 ExecutorService 文档中建议的关闭方法) - 有人可以向我解释为什么吗?
我的Server 是Runnable,我把它和我的单个客户端线程放在同一个ThreadPool 中——这可能是问题吗?
PS:这是一个大学项目。我故意省略了实现的接口和请求处理方法的名称,并重命名了类,以防止这成为将来每个懒惰学生的首选解决方案。
服务器
public class Server extends Runnable {
private final List<ClientHandler> activeHandlers = new ArrayList<>();
private int port;
private volatile boolean terminated = false;
private ExecutorService service;
@Override
public void start(int port) throws ServerException {
this.port = port;
service = Executors.newCachedThreadPool();
service.submit(this);
}
@Override
public void shutdown() throws ServerException {
System.out.println("Shutdown initiated.");
this.terminated = true;
PoolUtil.safeShutdown(service);
}
@Override
public void run() {
try (ServerSocket serverSocket = new ServerSocket(port)) {
while (!terminated) {
try {
Socket client = serverSocket.accept();
ClientHandler clientSocket = connect(client);
service.submit(clientSocket);
} catch (IOException e) {
System.err.println("ERROR: Connection to client failed.");
}
}
} catch (IOException e) {
System.err.println("ERROR: Could not create a socket on port " + port);
} finally {
PoolUtil.safeShutdown(service);
}
}
@Override
public ClientHandler connect(Socket client) {
ClientHandler clientHandler = new ClientHandler(client, this);
activeHandlers.add(clientHandler);
System.out.println("Registered new ClientHandler for " + client.getInetAddress().toString());
return clientHandler;
}
@Override
public void disconnect(ClientHandler clientHandler) {
activeHandlers.remove(clientHandler);
System.out.println("Client successfully disconnected.");
}
}
客户端处理程序
ublic class ClientHandler extends Runnable {
private final Socket client;
private final DirectoryServer server;
private boolean terminated;
private final Result result = new Result();
public ClientHandler(Socket client, DirectoryServer server) {
this.client = client;
this.server = server;
terminated = false;
}
@Override
public void run() {
try (client;
ObjectOutputStream oos = new ObjectOutputStream(client.getOutputStream());
ObjectInputStream ois = new ObjectInputStream(client.getInputStream())) {
while (!terminated) {
Object message = ois.readObject();
if (message instanceof SomeRequest) {
// dostuff...
} else if (message instanceof TerminateConnection) {
TerminateConnection termination = (TerminateConnection) message;
process(termination);
} else {
System.err.println(
"ERROR: the received object of class "
+ message.getClass().toString()
+ "can not be processed."
);
}
}
} catch (IOException e) {
// FIXME: Error handling
System.err.println("ERROR concerning client " + client.getInetAddress() + " -> " + e.getMessage());
} catch (ClassNotFoundException e) {
// FIXME: Error handling
System.err.println("ERROR: the class of the received object unknown to server --> " + e.getMessage());
}
}
@Override
public void process(TerminateConnection terminateConnection) {
this.terminated = true;
server.disconnect(this);
}
}
ServerMain
public class ServerMain {
public static void main(String[] args) throws ServerException, IOException {
Server server = new Server();
server.start(1337);
System.out.println("Server started. Press enter to terminate.");
System.in.read();
server.shutdown();
System.out.println("Server is shut down...");
}
}
PoolUtil.shutdown()
public static void safeShutdown(ExecutorService threadPool){
threadPool.shutdown();
try {
// Waits a minute for all tasks to terminate
if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {
// Cancel all tasks that are still running after a minute
threadPool.shutdownNow();
// Waits another minute for all tasks to be cancelled
if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {
System.err.println("Service did not terminate!");
}
}
} catch (InterruptedException e) {
threadPool.shutdownNow();
Thread.currentThread().interrupt();
}
}
【问题讨论】:
-
shutdownNow()后面一定要awaitTermination()吗?我见过在shutdownNow()之后没有awaitTermination()而在shutdown()之后没有shutdown()的例子。 -
@AliasCartellano 这就是Oracle proposes to shutdown ExecutorServices 的方式。我想额外的
awaitTermination()是一种额外的线程安全层——尽管我也不明白它的必要性。
标签: java multithreading threadpool executorservice shutdown