【问题标题】:Java: Closing multiple threads properlyJava:正确关闭多个线程
【发布时间】:2013-02-13 23:53:43
【问题描述】:

我正在制作一个简单的服务器,它将产生多个线程来处理多个客户端。我想知道在服务器终止时关闭和关闭所有各种流和线程的正确方法。

我添加了一个shutdownHook,它运行一个告诉服务器关闭的方法。反过来,服务器将关闭调用广播到它已打开的所有线程,这会将每个线程中的“isClosed”布尔值设置为 true。

我期望的是,每个线程在到达 run() 方法的末尾并再次循环时,会遇到 while(!isClosed) 条件,从而通过关闭所有正确的套接字/流来正确终止自己和回来了。

但是,我不知道这是否会正确关闭所有内容,因为程序应该在 shutdownhook 完成后终止。它完成得相当早,因为它所做的只是传播结束消息。这是否意味着某些线程没有足够的时间正确关闭?

如果是这样,最好的方法是让shutdownhook手动关闭每个线程,确保它们在返回之前已经关闭?

【问题讨论】:

  • 基本方法没有错,但您也可以查看Thread.interrupt(),以防您的线程中有任何阻塞调用。 (请注意,并非所有此类调用都是可中断的 - 我相信常规套接字读取不是。)
  • 如果它们是非守护线程,那么 JVM 在所有线程都从它们的 run 方法返回之前不会退出,无论 shutdownHook 方法是否返回。
  • 使用 executorservice 管理您的线程并在完成后关闭 executorservice。

标签: java multithreading sockets


【解决方案1】:

如果服务器终止,线程可能没有足够的时间正确终止,这是正确的。但是,根据您要执行的操作,这可能是也可能不是问题。如果不需要清理工作,那么您可能不需要担心它,因为让线程突然终止不会导致任何问题。

但是,如果需要完成清理工作(例如写入数据库),那么您需要做一些其他的事情。最好的方法(在 Java 中)是使用 Executor/ExecutorService 和相关项目(http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/Executors.html)。这些可以很好地解决您的问题,另外您还可以获得一些不错的免费赠品,例如线程池管理,因此扩展更加容易。例如,如果您为每个客户端生成一个新线程,那么稍后尝试扩展时就会遇到大问题,因为您不能每分钟创建一百万个线程。

如果您习惯使用原始线程,则使用 E​​xecutor 的东西有点调整,但值得研究。祝你好运!

【讨论】:

  • 如果我理解这一点,我只需使用 Executors 工厂创建一个 ExecutorService。然后我用执行添加我的线程,服务为我管理它?然后关闭,我在关闭应用程序时使用shutdown和shutdownNow的组合来清理线程?
  • 是的,你是对的。在构建线程时,您将希望正确处理 InterruptedException,以便如果将其抛出到您的线程上,则会进行适当的清理。捕获 InterruptedException 后未重新评估其终止条件的线程可能无法使用 ExecutorService 正常终止。如果您想确保在退出应用程序之前正常终止,您可以通过在主线程中使用 ExecutorService.isTerminated() 或 awaitTermination() 轻松检查它们的状态。希望有帮助!
【解决方案2】:

使用ExecutorService 是现代的做法。它从代码中消除了很多繁琐的部分。

Here 是一个很好的起点。

【讨论】:

  • 非常感谢。 ExecutorService 似乎是实现这一点的正确方法。
【解决方案3】:

shutdownHook 在循环中发生得太晚,无法以这种方式发挥作用。预计会很快完成,并且 JVM 已经处于关闭状态,如果它们是守护进程,它可能会占用现有线程。

我只想在连接线程上设置一个 15-30 秒的读取超时。如果超时发生(SocketTimeoutException),关闭套接字并退出线程。客户端当然必须处理掉线的连接,但他们必须已经这样做了。然后,当您想关闭时,只需停止接受新连接(例如,关闭 ServerSocket 并让其接受线程正确处理产生的异常)。当所有现有的连接线程都退出时,JVM 将退出,这实际上应该不会超过超时时间加上最长事务的长度。确保连接线程不是守护进程。

如果您不介意客户在交易过程中被砍掉,请致电System.exit().

【讨论】:

    【解决方案4】:

    您是否考虑过让您的线程成为守护线程。 只需添加 t.setdaemon(true);在调用线程的 start 方法之前。 如果这些线程应该在程序结束时结束,那么一旦所有其他非守护线程结束,使它们成为守护进程就会杀死它们。 线程池中使用的线程是应该是守护进程的线程的好例子。 我真的认为它对你有用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-04-30
      • 2014-04-19
      • 2023-03-28
      • 2011-04-26
      • 2013-02-27
      • 2014-06-11
      • 1970-01-01
      相关资源
      最近更新 更多