[英]Graceful shutdown of multithreaded java server using executor service
我重構了一個單線程服務器,使其能夠同時處理多線程和接受多個客戶端。 為此,我為每個新客戶端生成一個新的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);
}
}
主服務器
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();
}
}
ExecutorService.shutdown()方法的 JavaDoc 聲明這意味着“之前提交的任務已執行,但不會接受新任務”。 但是,在所有任務完成之前不會終止。 您的任務的 Runnables 執行阻塞操作serverSocket.accept()
因此您不應期望 awaitTermination 方法在關閉后返回,直到關閉后有足夠的請求進入以用完所有阻塞的任務。 您可以嘗試使用 shutdownNow() 而不是 shutdown() 以便它嘗試立即取消/中斷所有正在運行的任務,這有望解除它們的阻塞。
感謝@njr ,我意識到serverSocket.accept()
是我問題的根源。
該方法正在阻塞並等待傳入連接。 為了能夠終止它,我使serverSocket
成為一個實例變量,我可以通過在我的shutdown()
方法中調用serverSocket.close()
來關閉它。
這將導致serverSocket.accept()
拋出IOException
- 所以我抓住它並調用Thread.currentThread().interrupt()
來關閉正在運行的線程。
下面是相關代碼:
public class Server extends Runnable {
private final List<ClientHandler> activeHandlers = new ArrayList<>();
private ServerSocket newConnections;
private volatile boolean terminated = false;
private ExecutorService service;
@Override
public void start(int port) throws ServerException {
try {
this.newConnections = new ServerSocket(port);
service = Executors.newCachedThreadPool();
service.submit(this);
} catch (IOException e) {
throw new ServerException("Server can not be created at port " + port);
}
}
@Override
public void shutdown() throws ServerException {
try {
this.terminated = true;
newConnections.close();
} catch (IOException e) {
throw new ServerException("Shut down failed - server socket can not be closed");
} finally {
PoolUtil.safeShutdown(service);
}
}
@Override
public void run() {
try {
while (!terminated) {
try {
Socket client = newConnections.accept();
ClientHandler clientSocket = connect(client);
service.submit(clientSocket);
} catch (IOException e) {
System.out.println("ServerSocket terminated");
Thread.currentThread().interrupt();
}
}
} finally {
PoolUtil.safeShutdown(service);
}
}
// left out irrelevant methods
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.