中断超过阈值的线程的最佳做法

时间:2013-08-30 07:03:33

标签: java multithreading threadpool executorservice

我正在使用Java ExecutorService框架来提交可执行的可调用任务。 这些任务与Web服务通信,并且应用了5分钟的Web服务超时。 但是我已经看到,在某些情况下,超时被忽略并且线程'挂起'在API调用上 - 因此,我想取消所有花费的时间超过5分钟的任务。

目前,我有一个期货清单,我会遍历它们并调用future.get直到所有任务完成。现在,我已经看到future.get重载方法占用超时并在任务未在该窗口中完成时抛出超时。所以我想到了一种方法,我使用超时执行future.get(),并且在TimeoutException的情况下,我执行future.cancel(true)以确保此任务被中断。

我的主要问题
 1.超时的获取是否是解决此问题的最佳方法?  2.是否有可能我正在等待一个尚未放置在线程池上的get调用(不是活动工作者)。在这种情况下,我可能正在终止一个线程,当它启动时可能在规定的时限内实际完成?

任何建议都将深表感谢。

4 个答案:

答案 0 :(得分:2)

  1. 延迟获取是否是解决此问题的最佳方法?

    • 这还不够。例如,如果您的任务不是为了响应中断而设计的,那么它将继续运行或被阻止
  2. 是否有可能我正在等待一个尚未放置在线程池上的get调用(不是活动工作者)。在这种情况下,我可能正在终止一个线程,当它启动时可能在规定的时限内实际完成?

    • 是的,如果您的线程池配置不正确,您最终可能会取消从未计划运行的任务
  3. 当您的任务包含不可中断阻止时,以下代码段可能是让您的任务响应中断的方式之一。它也不会取消未安排运行的任务。这里的想法是通过关闭套接字,数据库连接等来覆盖中断方法和关闭运行任务。这段代码并不完美,您需要根据需求进行更改,处理异常等。

    class LongRunningTask extends Thread {
    private  Socket socket; 
    private volatile AtomicBoolean atomicBoolean;
    
    
    public LongRunningTask() {
        atomicBoolean = new AtomicBoolean(false);
    }
    
    @Override
    public void interrupt() {
        try {
            //clean up any resources, close connections etc.
            socket.close();
        } catch(Throwable e) {
        } finally {
            atomicBoolean.compareAndSet(true, false);
            //set the interupt status of executing thread.
            super.interrupt();
        }
    }
    
    public boolean isRunning() {
        return atomicBoolean.get();
    }
    
    @Override
    public void run() {
        atomicBoolean.compareAndSet(false, true);
        //any long running task that might hang..for instance
        try {
            socket  = new Socket("0.0.0.0", 5000);
            socket.getInputStream().read();
        } catch (UnknownHostException e) {
        } catch (IOException e) {
        } finally {
    
        }
    }
    }
    //your task caller thread
    //map of futures and tasks 
        Map<Future, LongRunningTask> map = new HashMap<Future, LongRunningTask>();
        ArrayList<Future> list = new ArrayList<Future>();
        int noOfSubmittedTasks = 0;
    
        for(int i = 0; i < 6; i++) {
            LongRunningTask task = new LongRunningTask();
            Future f = execService.submit(task);
            map.put(f, task);
            list.add(f);
            noOfSubmittedTasks++;
        }
    
        while(noOfSubmittedTasks > 0) {
            for(int i=0;i < list.size();i++) {
                Future f = list.get(i);
                LongRunningTask task = map.get(f);
                if (task.isRunning()) {
                    /*
                     * This ensures that you process only those tasks which are run once
                     */
                    try {
                        f.get(5, TimeUnit.MINUTES);
                        noOfSubmittedTasks--;
                    } catch (InterruptedException e) {
                    } catch (ExecutionException e) {
                    } catch (TimeoutException e) {
                                                //this will call the overridden interrupt method
                        f.cancel(true);
                        noOfSubmittedTasks--;
                    }
                }
    
            }
        }
        execService.shutdown();
    

答案 1 :(得分:0)

  

超时获取是解决此问题的最佳方法吗?

是的,对于Future对象get(timeout)完全没问题,如果未来指向的任务已经执行,它将立即返回。如果任务尚未执行或正在执行,那么它将等到超时并且是一个很好的做法。

  

是否有可能在等待任务的get调用   尚未被放置在线程池中(不是活跃的工作者)

只有在线程池上放置任务时才会得到Future对象,因此无法在任务上调用get()而不将其放在线程池上。是的,有可能自由工作者尚未完成任务。

答案 2 :(得分:0)

你说的方法还可以。但最重要的是,在设置超时阈值之前,您需要知道线程池大小和环境的最佳值是什么。做一个压力测试,它会揭示你配置为Threadpool一部分的工作线程是否合适。这甚至可以减少超时值。所以这个测试对我来说是最重要的。

get的超时完全正常但是如果它抛出TimeoutException,你应该添加取消任务。如果您正确执行上述测试并将线程池大小和超时值设置为理想值,则甚至可能无需在外部取消任务(但您可以将其作为备份)。是的,有时在取消任务时,您可能最终取消执行者尚未接收的任务。

答案 3 :(得分:0)

您当然可以使用

取消任务
  

task.cancel(真)

这完全合法。但是如果它是“RUNNING”,这会中断线程。

如果线程正在等待获取内部锁定,那么除了设置线程的中断状态之外,“中断”请求没有任何效果。 在这种情况下,你无法做任何事情来阻止它。为了中断发生,线程应该通过获取它正在等待的锁来从“阻塞”状态出来(可能需要超过5个分钟)。这是使用“内在锁定”的限制。

但是,您可以使用显式锁定类来解决此问题。您可以使用“Lock”接口的“lockInterruptibly”方法来实现此目的。 “lockInterruptibly”将允许线程尝试获取锁定,同时保持对中断的响应。以下是实现这一目标的一个小例子:

public void workWithExplicitLock()throws InterruptedException{
Lock lock = new ReentrantLock();
lock.lockInterruptibly()();
try {
// work with shared object state
} finally {
lock.unlock();
}

}