【问题标题】:Terminating application and ScheduledExecutorService; Thread终止应用程序和 ScheduledExecutorService;线
【发布时间】:2012-05-30 19:14:18
【问题描述】:

我有以下逻辑(简化):

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();
    }

}

我想要实现的是以正确的方式终止Threads。首先,这是 (switchStopServer()) 的正确方法吗,还是有更好的解决方案?

我对@9​​87654321@ 有点困惑,因为shutdownNow() 不会打断Threads,ScheduledFuture.cancel(true) 也不会(至少对我来说不会),所以我不能打断ServerSocket.accept()。我知道,在我的示例中不需要ScheduledExecutorService,但在我的实际应用程序中是有的。

【问题讨论】:

  • 你为什么要以预定的方式监听 ServerSocket?有什么具体原因吗?如果您有一个专用线程在紧密循环中侦听 ServerSocket 并处理与新衍生线程或执行器服务的新连接,则设计可能会简单得多。
  • @Zaki 我想,因为我以预定的方式启动其他线程,所以我将使用同一个池来启动所有线程,所以当需要终止时,我只需调用shutdownNow() .但你是对的,我根本不应该安排它们。谢谢!

标签: java multithreading scheduled-tasks


【解决方案1】:

我认为您的问题是您混淆了ThreadRunnable。尽管ListenerThread 扩展了Thread,但它实际上不是它自己的线程。该线程由 ExecutorService 线程池管理,它只是调用您的 run() 方法。这只是[某种]工作,因为Thread 也实现了Runnable。当您调用 ListenerThread.interrupt() 时,尽管您正在调用 interrupt() 方法,但您并没有中断线程池中的线程,而只是直接在调用线程中。这应该关闭套接字,因为它从外部调用closeServerSocket()

当你调用ScheduledFuture.cancel(true)shutdownNow() 时,pool 线程应该被中断,但这不会在那里调用你的interrupt() 方法。您可以在 run() 方法中使用 Thread.currentThread().isInterrupted() 来测试中断。

您应该将 ListenerThread 从扩展 Thread 更改为只实现 Runnable请参阅下面的编辑)。您需要在 run() 方法中执行类似以下循环的操作:

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

要中断accept() 方法,您将不得不从另一个线程关闭serverSocket。这很可能由调用interrupt() 的线程完成。它应该关闭套接字,shutdownNow()cancel() 线程池,然后它可以等待池终止。

编辑:

实际上,我想知道您为什么要为您的ListenerThread 使用一个池,因为它们中只有一个,它会立即被安排,并且它只是直接在任何连接上启动一个新线程。我会完全删除您的 taskScheduler 池,保留 ListenerThread 扩展 Thread,然后调用 new ListenerThread().start();

外线程仍然会关闭serverSocket 以停止ListenerThread。如果您还需要关闭所有连接,那么ListenerThread 需要保留socketConnection 的集合,以便当accept() 抛出IOException 时它可以在它们上调用close()

另外,目前您拥有private Socket socketConnection;,这是一种误导,因为它会在每次调用accept() 后发生变化。我将其重写为:

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

【讨论】:

  • 还是有点迷茫。在调试中实际调用了中断方法,应用程序成功终止。如果换成Runnable,应该怎么打断accept()命令?
  • 是的,对不起@Daniel。 interrupt() 被调用但直接在调用线程中——而不是来自线程池中的线程。这意味着您将遇到同步问题。
  • @Daniel 您将需要从线程外部关闭serverSocket - 很可能来自尝试执行中断的同一线程。
  • 当我更多地处理您的代码时,对我的答案@Daniel 进行更多编辑。
  • 谢谢,很好的建议。检查我在问题下方的评论,它解释了调度程序。跟踪Threads 并在需要时致电closeServerSocket() 的正确方法是什么?我的意思是,将它们存储在静态变量中闻起来很脏。我是否应该为此创建一个单独的Thread
猜你喜欢
  • 1970-01-01
  • 2011-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-24
  • 1970-01-01
  • 1970-01-01
  • 2014-09-24
相关资源
最近更新 更多