简体   繁体   中英

Terminating application and ScheduledExecutorService; Thread

I have the following logic (simplified):

public class Application {

    public static volatile boolean stopServer;
    private static ScheduledExecutorService taskScheduler;

    private static Thread listenerThread;

    public static synchronized void switchStopServer() {
        stopServer = true;

        listenerThread.interrupt();
        taskScheduler.shutdownNow();
    }

    public static void main(String[] args) {
            int threadPoolSize = 4;
            taskScheduler = Executors.newScheduledThreadPool(threadPoolSize);

            listenerThread = new ListenerThread();
            taskScheduler.schedule(listenerThread, 0, TimeUnit.NANOSECONDS);
    }

}

public class ListenerThread extends Thread {

    private static ServerSocket serverSocket;
    private Socket socketConnection;

    @Override
    public void run() {
         while (!Application.stopServer) {
              try {
                   socketConnection = serverSocket.accept();
                   new CommunicatorThread(socketConnection).start();
              } catch (SocketException e) {
              } catch (Exception e) {
              }
         }  
    }

    private static void closeServerSocket() {
         try {
              if (serverSocket != null && !serverSocket.isClosed()) serverSocket.close();
         } catch (Exception e) { }
    }

    @Override
    public void interrupt() {
         closeServerSocket();
         super.interrupt();
    }

}

What I want to achieve, is to terminate Thread s the proper way. First of all, is this ( switchStopServer() ) the correct way to do that, or are there any better solutions?

I'm a little confused with the ScheduledExecutorService , because shutdownNow() does not interrupt the Thread s, neither does ScheduledFuture.cancel(true) (at least for me it doesn't), so I can't interrupt ServerSocket.accept() . I know, in my example there is no need for the ScheduledExecutorService , but in my real application there is.

Your problem I believe is that you are confusing Thread and Runnable . Even though ListenerThread extends Thread , it is actually not it's own thread. The thread is managed by the ExecutorService thread-pool which is just calling your run() method. This is only [sort of] working because Thread also implements Runnable . When you call ListenerThread.interrupt() you are not interrupting the thread in the thread-pool although you are calling your interrupt() method but just directly in the calling thread. This should close the socket since it calls closeServerSocket() from the outside.

When you call ScheduledFuture.cancel(true) or shutdownNow() , the pool thread(s) should be interrupted but this will not call your interrupt() method there. You can test for the interruption by using Thread.currentThread().isInterrupted() in your run() method.

You should change ListenerThread from extending Thread and instead have it just implement Runnable ( see edit below ). You will want to do something like the following loop in your run() method:

while (!Application.stopServer && !Thread.currentThread().isInterrupted()) {

To interrupt the accept() method, you are going to have to close the serverSocket from another thread. Most likely this will be done by the thread that is calling interrupt() . It should close the socket, shutdownNow() or cancel() the thread-pool, and then it can wait for the pool to terminate.

Edit:

Actually, I wonder why you are using a pool for your ListenerThread since there will only ever be one of them, it is being scheduled immediately, and it is just starting a new thread on any connection directly. I would remove your taskScheduler pool entirely, keep ListenerThread extending Thread , and just call new ListenerThread().start(); .

The outer thread would still just close the serverSocket to stop the ListenerThread . If you also need to close all of the connections as well then the ListenerThread needs to keep a collection of the socketConnection around so it can call close() on them when the accept() throws an IOException .

Also, currently you have private Socket socketConnection; which is misleading because it will change after every call to accept() . I'd rewrite it as:

 Socket socketConnection = serverSocket.accept();
 new CommunicatorThread(socketConnection).start();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM