簡體   English   中英

使用執行器服務優雅關閉多線程 java 服務器

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM