【问题标题】:How to end a thread in executor service if thread takes too long?如果线程花费太长时间,如何结束执行器服务中的线程?
【发布时间】:2017-11-18 07:58:15
【问题描述】:

示例执行器服务

 static class MyRunnable implements Runnable {

    private String serverName;

    public MyRunnable(String serverName) {
        super();
        this.serverName = serverName;
    }

    @Override
    public void run() {
        ...
        conn = new ch.ethz.ssh2.Connection(serverName);
        conn.connect();

        boolean isAuthenticated = conn.authenticateWithPassword(user, pass);
        logger.info("Connecting to " + server);

        if (isAuthenticated == false) {
            logger.info(server + " Please check credentials");
        }

        sess = conn.openSession();
        ...

    }

}

public static void main(String[] args) {
    List<String> serverList = ...;
    ExecutorService executor = Executors.newFixedThreadPool(20);

    for (String serverName : serverList) {
        MyRunnable r = new MyRunnable(serverName);
        executor.execute(r);
    }

    executor.shutdown();
    executor.awaitTermination(1, TimeUnit.HOURS);
}

这里是我的执行器服务的示例代码。但是,当我遇到无法连接或连接时间过长的服务器时,使用这种逻辑会在我的应用程序中创建一个挂起时间。如果连接时间超过 x 时间,我想结束/终止线程。如果线程任务在 2 秒内没有连接到服务器,我该如何终止它。

尝试

       ThreadPoolExecutor executor = new ThreadPoolExecutor(
                10, 25, 500, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(1));

我添加了以下代码,但如果它花费的时间超过 2000 毫秒,显然它不会结束线程。

尝试 2

Future<?> future = executor.submit( new task));
            try {
                future.get(2000, TimeUnit.MILLISECONDS); // This waits timeout seconds; returns null
            }

            catch(TimeoutException e) {
                future.cancel(true);
               // System.out.println(server + "name");
            } 

【问题讨论】:

  • 关闭来自另一个线程的连接导致Runnable异常。
  • 还有另一种使用超时的连接方法。

标签: java multithreading ganymede ssh2-exec


【解决方案1】:

如果线程任务在 2 秒内没有连接到服务器,我该如何终止它。

这通常很难做到,因为即使您中断线程(就像提到的其他答案一样),也不能保证线程会停止。中断只是在线程上设置一个标志,由代码来检测状态并停止。这意味着大量线程可能在后台等待连接。

但是,在您的情况下,您使用的是 ch.ethz.ssh2.Connection.connect() 方法。原来有一个connect method that takes a timeout。我认为您想要以下内容:

// try to connect for 2 seconds
conn.connect(null, 2000, 0);

引用connect method javadocs

如果发生超时(connectTimeout 或 kexTimeout),则会抛出 SocketTimeoutException。

【讨论】:

  • 我假设 null 应该是我的服务器/主机名? API 显示它是一个验证程序,我应该将其保留为空吗?
  • 我看到@Jesse 是connect() 调用与connect(null, 0,0) 相同。我认为它是一个验证者。
  • 非常感谢先生。我一直在浏览 API,但这个方法却让我看不到了。它按预期工作。
【解决方案2】:

你必须先做awaitTermination(),然后检查返回值,然后再做shutdownNow()shutdown() 不保证服务会立即停止,它只是停止接受新作业,并等待所有作业按顺序完成。另一方面,shutdownNow() 停止接受新任务,主动尝试停止所有正在运行的任务,并且不启动任何新任务,返回所有等待执行任务的列表。

来自JavaDocs

以下方法分两个阶段关闭一个ExecutorService, 首先通过调用shutdown来拒绝传入的任务,然后调用 shutdownNow,如有必要,取消任何延迟任务:

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

【讨论】:

  • 为什么-1 人?答案似乎是合法的
【解决方案3】:

你可以随时调用 future.get(timeout...) 如果它还没有完成,它将返回超时异常......然后你可以调用future.cancel()。

【讨论】:

    【解决方案4】:

    只要您在 Java 中处理线程,停止线程的唯一安全方法就是中断它。您可以先拨打shutdown(),然后等待。此方法不会中断线程。

    如果没有帮助,则调用shutdownNow(),它会通过将每个线程的中断标志设置为真来尝试取消任务。在这种情况下,如果线程被阻塞/等待,则会抛出 InterruptedException。如果您在任务中的某处检查中断标志,那么您也很好。

    但是,如果您别无选择,只能停止线程,您仍然可以这样做。访问工作线程的一种可能解决方案是在自定义线程工厂的帮助下跟踪 ThreadPoolExecutor 中所有创建的线程。

    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.*;
    
    public class TestThreadPoolEx {
    
        static class CustomThreadFactory implements ThreadFactory {
            private List<Thread> threads = new ArrayList<>();
    
            @Override
            public Thread newThread(Runnable r) {
                Thread t = new Thread(r);
                threads.add(t);
                return t;
            }
    
            public List<Thread> getThreads() {
                return threads;
            }
    
            public void stopThreads() {
                for(Thread t : threads) {
                    if(t.isAlive()) {
                        try {
                            t.stop();
                        } catch (Exception e) {
                            //NOP
                        }
                    }
                }
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            CustomThreadFactory factory = new CustomThreadFactory();
            ExecutorService ex = Executors.newFixedThreadPool(1, factory);
            ex.submit(() -> {
                while(true);
            });
            ex.shutdown();
            ex.awaitTermination(5, TimeUnit.SECONDS);
            ex.shutdownNow();
            ex.awaitTermination(5, TimeUnit.SECONDS);
            factory.stopThreads();
        }
    }
    

    这肯定不安全,但应该符合您的要求。在这种情况下,它能够停止 while(true) 循环。取消任务将无法做到这一点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-07-29
      • 2010-11-12
      • 2014-09-10
      • 1970-01-01
      • 2019-08-24
      • 1970-01-01
      • 1970-01-01
      • 2021-05-01
      相关资源
      最近更新 更多