Java ExecutorService 暂停/恢复特定线程

Posted

技术标签:

【中文标题】Java ExecutorService 暂停/恢复特定线程【英文标题】:Java ExecutorService pause/resume a specific thread 【发布时间】:2012-08-08 14:24:05 【问题描述】:

有没有办法使用 ExecutorService 来暂停/恢复特定线程?

private static ExecutorService threadpool = Executors.newFixedThreadPool(5);

想象一下,我想停止 id=0 的线程(假设为每个线程分配一个递增的 id,直到达到线程池的大小)。

过了一会儿,通过按下一个按钮,假设我想恢复该特定线程,并让所有其他线程保持当前状态,可以暂停或恢复。

我在 Java 文档中发现了一个未完成的 PausableThreadPoolExecutor 版本。但它不适合我的需要,因为它会恢复池中的所有线程。

如果没有办法使用 ExecutorService 的默认实现来解决这个问题,谁能指点我的 Java 实现来解决这个问题?

【问题讨论】:

你确定吗?具有给定 id 的线程执行一些随机选择的作业,因此您正在停止一些未知的作业。这样做有什么意义? 想象以下情况:您有一个下载管理器,您可以在该管理器上停止和恢复下载。对于您正在下载的每个内容,无论什么不相关,并且您希望停止该下载以在任何您想要的地方恢复。这会让你的问题更清楚吗? 比较清楚,但还是没有多大意义。停止/恢复下载是关于业务逻辑的,而线程池是关于计算资源的。恕我直言,通过添加/删除资源来控制逻辑是个坏主意。 【参考方案1】:

你走错了路。线程池拥有线程,通过与您的代码共享它们可能会搞砸事情。 您应该专注于使您的任务(传递给可取消/可中断的线程),而不是直接与池拥有的线程交互。 此外,当您尝试中断线程时,您不会知道正在执行什么作业,所以我不明白您为什么有兴趣这样做

更新: 取消在线程池中提交的任务的正确方法是通过执行程序返回的任务的Future。 1)通过这种方式,您可以确定您实际瞄准的任务被尝试取消 2)如果您的任务已经设计为可取消,那么您已经完成了一半 3) 不要使用标志来表示取消,而是使用Thread.currentThread().interrupt()

更新:

public class InterruptableTasks   

    private static class InterruptableTask implements Runnable  
        Object o = new Object();  
        private volatile boolean suspended = false;  

        public void suspend()          
            suspended = true;  
          

        public void resume()       
            suspended = false;  
            synchronized (o)   
                o.notifyAll();  
              
          


        @Override  
        public void run()   

            while(!Thread.currentThread().isInterrupted())  
                if(!suspended)  
                    //Do work here      
                
                else  
                    //Has been suspended  
                    try                    
                        while(suspended)  
                            synchronized(o)  
                                o.wait();  
                                                       
                                               
                      
                    catch (InterruptedException e)                     
                                 
                                           
              
            System.out.println("Cancelled");        
        

    

    /**  
     * @param args  
     * @throws InterruptedException   
     */  
    public static void main(String[] args) throws InterruptedException   
        ExecutorService threadPool = Executors.newCachedThreadPool();  
        InterruptableTask task = new InterruptableTask();  
        Map<Integer, InterruptableTask> tasks = new HashMap<Integer, InterruptableTask>();  
        tasks.put(1, task);  
        //add the tasks and their ids

        Future<?> f = threadPool.submit(task);  
        TimeUnit.SECONDS.sleep(2);  
        InterruptableTask theTask = tasks.get(1);//get task by id
        theTask.suspend();  
        TimeUnit.SECONDS.sleep(2);  
        theTask.resume();  
        TimeUnit.SECONDS.sleep(4);                
        threadPool.shutdownNow();      
    

【讨论】:

您能否更具体一些,并提供一个真实的实现或示例?我真的很感激,因为我在这个问题上苦苦挣扎了很长时间。 @RicardoSantos:但是你为什么不让你的任务可以取消呢?与你的代码不直接拥有的线程交互并不是一个好主意 我同意让任务可取消的建议。 OP 写道“想象一下我想停止线程 id=0”。这没有多大意义,因为在任何给定时间你都不知道线程 0 在做什么。但是谈论停止当前正在完成的一些工作确实有意义,因此建议取消任务。 实际上我的线程正在使用一个标志被取消。在我的 while 循环迭代中,我使用该标志,直到我将其设置为 false。所以是的,我的 Runnables 是可取消的。问题是,如果我将标志设置为 false 并停止执行,检查线程状态我可以看到我的线程处于等待状态。这有点酷。现在我遇到的问题是如何恢复他们的工作?并且请不要回复“将标志设置为以前的状态”,因为这不起作用 @user384706 看起来与我正在寻找的完全一样,除了一个部分。您正在通知所有线程,这意味着如果我有 2 个任务暂停(id=1 和 id=2)并且想要恢复 id=2 的任务,您将通知所有线程(id=1 和 id=2) .我说的对吗?【参考方案2】:

建议:与您使用的标志类似/代替您使用的标志,为您需要暂停/取消暂停的每个任务创建一个带有 1 个许可 (new Semaphore(1)) 的 semaphore。在任务的工作循环的开头放这样的代码:

semaphore.acquire();
semaphore.release();

这会导致任务获取信号量许可并立即释放它。现在,如果您想暂停线程(例如按下按钮),请从另一个线程调用semaphore.acquire()。由于信号量现在有 0 个许可,您的工作线程将在下一个周期开始时暂停并等待,直到您从另一个线程调用 semaphore.release()

(如果您的工作线程在等待时被中断,acquire() 方法会抛出 InterruptedException。还有另一种方法 acquireUninterruptibly(),它也尝试获取许可,但不会被中断。)

【讨论】:

当然可以,但听起来更像是一种 hack,它是比什么都不做更好的方法。所以谢谢你的回答。我会测试你的方法和@user384706,看看哪个性能更好。【参考方案3】:

一种情况可能是,一个人想要模拟多个设备。设备具有功能。总的来说,这组设备同时运行。现在,如果一个线程代表一个设备(或一个线程代表一个设备的一个功能),则可能想要控制设备的生命周期,例如start(), shutdown(), resume()

【讨论】:

如果您使用的是池,那么线程是可互换的,并且不会映射到设备。最好的建议是让任务可以取消,不要对线程进行微观管理。

以上是关于Java ExecutorService 暂停/恢复特定线程的主要内容,如果未能解决你的问题,请参考以下文章

ExecutorService线程池中怎么去暂停和继续一个线程

java并发中ExecutorService的使用

Java线程池 ExecutorService

Java基础ExecutorService的使用

Java线程池 ExecutorService了解一下

Java 中这段代码中的 ExecutorService.submit 和 ExecutorService.execute 有啥区别?