【问题标题】:Executor Service setting the flag to stop the threadExecutor Service 设置标志停止线程
【发布时间】:2017-05-16 05:57:34
【问题描述】:

我正在运行简单的thread,其运行方法如下

public run()
while(!stopFlag){
   // print something Line 1
   // print something Line 2
   // print something Line 3
   // print something Line 4
}

如果我通过ExecutorService 运行这个线程

ExecutorService exs = Executors.newFixedThreadPool(5);
exs.execute(new MyThread));

我停止ExecutorService

exs.shutdown();

但这不会停止线程,因为标志未设置为 false。在another question related to same topic 中,我被要求正确处理调用 exs.shutdown() 时引起的 InterruptedException。 但在这种情况下,我没有做任何可能引发 InterruptedException 的操作。

处理这种情况的标准方法是什么?

更多问题 Sabir 给出的答案是“如果您的可运行对象对中断的响应不佳,那么除了关闭 JVM 之外,没有什么可以阻止它。”这似乎是我的情况。

但是如何引入InterruptedException的处理;如果我没有调用任何引发中断异常的方法?

【问题讨论】:

  • 您没有显示足够的线程代码。此外,您显示的那一点不会编译。
  • 检查线程是否被中断。即while(!Thread.interrupted())
  • 文档说:This method does not wait for previously submitted tasks to * complete execution. Use awaitTermination * to do that.
  • 最主要的是,当您使用阻塞 api 时,它会抛出一个中断的异常。如果您没有使用阻塞 api,则必须检查线程是否被中断。然后如果它被打断了,退出你的循环,或者如果你愿意,甚至抛出一个被打断的异常。

标签: java multithreading threadpool executorservice


【解决方案1】:

如果您愿意关闭您的线程,即使该标志仍然为真,您应该使用 - ExecutorService.shutdownNow() 方法而不是 ExecutorService.shutdown()

引用自 Java 文档,

shutdown()

启动有序关闭,其中先前提交的任务被 执行,但不会接受新任务。调用没有 如果已经关闭,则附加效果。

此方法不等待之前提交的任务完成 执行。使用 awaitTermination 来做到这一点。

shutdownNow()

尝试停止所有正在执行的任务,停止处理 等待任务,并返回正在等待的任务列表 执行。

此方法不等待主动执行的任务终止。 使用 awaitTermination 来做到这一点。

除了尽力停止处理之外,没有任何保证 积极执行任务。例如,典型的实现将 通过 Thread.interrupt 取消,因此任何无法响应的任务 中断可能永远不会终止。

对于标准方式,我将引用来自ExecutorService接口的JDK示例,

用法示例

Here is a sketch of a network service in which threads in a thread pool service incoming requests. It uses the preconfigured Executors.newFixedThreadPool factory method:    class NetworkService implements Runnable {    private final ServerSocket serverSocket;    private final ExecutorService pool;

   public NetworkService(int port, int poolSize)
       throws IOException {
     serverSocket = new ServerSocket(port);
     pool = Executors.newFixedThreadPool(poolSize);    }

   public void run() { // run the service
     try {
       for (;;) {
         pool.execute(new Handler(serverSocket.accept()));
       }
     } catch (IOException ex) {
       pool.shutdown();
     }    }  }

 class Handler implements Runnable {    private final Socket socket;   Handler(Socket socket) { this.socket = socket; }    public void run() {
     // read and service request on socket    }  }} The following method shuts down an ExecutorService in two phases, first by calling shutdown to reject incoming tasks, and then calling shutdownNow, if necessary, to cancel any lingering tasks:    void shutdownAndAwaitTermination(ExecutorService pool) {    pool.shutdown(); // Disable new tasks from being submitted    try {
     // Wait a while for existing tasks to terminate
     if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
       pool.shutdownNow(); // Cancel currently executing tasks
       // Wait a while for tasks to respond to being cancelled
       if (!pool.awaitTermination(60, TimeUnit.SECONDS))
           System.err.println("Pool did not terminate");
     }    } catch (InterruptedException ie) {
     // (Re-)Cancel if current thread also interrupted
     pool.shutdownNow();
     // Preserve interrupt status
     Thread.currentThread().interrupt();    }  }}

请注意,即使使用 shutdownNow() 也无法保证。

编辑:如果我将您的while(!stopFlag) 更改为while(!Thread.currentThread().isInterrupted()),则带有条件循环的线程会使用shutdownNow() 而不是shutdown(),因此线程会被shutdownNow() 中断。我在 JDK8 和 Windows 8.1 上。我确实必须在主线程中休眠,以便服务有时间设置服务并启动可运行的。线程启动,进入,然后在调用shutdownNow() 时停止。 shutdown() 没有这种行为,即线程永远不会退出 while 循环。因此,应该通过检查标志或处理异常来使您的可运行对象负责中断的方法。如果你的 runnable 不能很好地响应中断,除了关闭 JVM 之外,没有什么可以阻止它。

here 展示了一个很好的方法

【讨论】:

  • 我已经用我看到的结果编辑了我的答案。 shutdownNow() 使用 while 循环中断线程。
  • “如果您的可运行对象对中断的响应不佳,那么除了关闭 JVM 外,没有什么可以阻止它。”....这似乎是我的情况。 :(
  • 但是如何引入对InterruptedException的处理;如果我没有调用任何引发中断异常的方法
  • 我不确定在那个 while 循环中执行了什么样的操作,但如果不调用任何抛出 InterruptedException 的方法,你就不能。您可以将您的run() 方法封装成类似private void runLogic() throws InterruptedException{} 的方法,通过从run() 调用runLogic() 来实现。另见this
【解决方案2】:

从你的问题来看,我假设你正试图优雅地关闭进程。为此,您需要注册一个shutdownHook 来实现它。这是实现它的示例代码。

package com.example;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadManager {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Runtime.getRuntime().addShutdownHook(new Thread(){
            MyThread myThread = null;
            @Override
            public void run(){
                System.out.println("Shutting down....");
                this.myThread.stopProcess();
            }
            public Thread setMyThread(MyThread myThread){
                this.myThread=myThread;
                return this;
            }
        }.setMyThread(myThread));
        ExecutorService exs = Executors.newFixedThreadPool(5);
        myThread.setName("User");
        exs.execute(myThread);
        exs.shutdownNow();
    }
}

在 MyThread.java 中将如下所示:-

package com.example;

public class MyThread extends Thread{
    private boolean stopFlag;

    @Override
    public void run(){
        while(!stopFlag){
           System.out.println(this.getName());
        }
    }
    public void stopProcess(){
        this.stopFlag=true;
    }
}

现在,如果您制作此代码的 jar 文件并在 Linux 服务器中运行以查看它是如何工作的,那么请执行这些附加步骤

Step 1> nohup java -jar MyThread.jar &

按 ctrl+c 存在 现在使用以下命令查找 pid

Step 2> ps -ef| grep MyThread.jar

获得 pid 后,执行以下命令优雅地停止

Step 3>kill -TERM <Your PID>

当您检查 nohub.out 文件时,输出将如下所示

User
User
.
.
.
User
Shutting down....
User
.
.

请记住,如果您尝试使用 kill -9 关闭,那么您将永远不会看到 Shutting down.... 消息。

@Sabir 已经讨论了shutdownshutdownNow 之间的区别。但是,我绝不会建议您在线程运行时使用interrupt 调用。在实时环境中可能会导致内存泄漏。

更新 1:-

public static void main(String[] args) {
    MyThread myThreads[] = new MyThread[5];
    ExecutorService exs = Executors.newFixedThreadPool(5);
    for(int i=0;i<5;++i){
        MyThread myThread = new MyThread();
        myThread.setName("User "+i);
        exs.execute(myThread);
        myThreads[i] = myThread;
    }
    Runtime.getRuntime().addShutdownHook(new Thread(){
        MyThread myThreads[] = null;
        @Override
        public void run(){
            System.out.println("Shutting down....");
            for(MyThread myThread:myThreads){
                myThread.stopProcess();
            }
        }
        public Thread setMyThread(MyThread[] myThreads){
            this.myThreads=myThreads;
            return this;
        }
    }.setMyThread(myThreads));
    exs.shutdownNow();
}

【讨论】:

  • 所以我必须创建与提交给 ExecutorService 的任务一样多的关闭挂钩。所以认为这是一种可能的技术方式;看起来并不优雅。从讨论看起来应该首先以优雅的方式实现 Thread 以避免这种讨厌的情况。
  • 使您的线程可取消完全是 Java 多线程中的一个特定部分,大多数程序员都错过了这部分。假设一个 UI 显示了 JVM 中的所有线程,并且您希望为线程提供一个 cancel 按钮,您会使用 addShutdownHook 吗?关闭钩子是 JVM 级别的概念,您永远不应该假设只有您的程序在该 JVM 中运行。
  • 不,您不必使用许多关闭挂钩。为什么不使用线程数组。
  • 请检查更新后的代码。是的,你必须小心线程的实现。 @Sabir 您可以在远程访问的情况下使用 JMX,但此示例只是桌面应用程序的 sudo 代码。
猜你喜欢
  • 2014-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-28
  • 2019-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多