Java ExecutorService pause/resume a specific thread

11,814

Solution 1

You are on the wrong track. The thread pool owns the threads and by sharing them with your code could mess things up.
You should focus on making your tasks (passed to the threads cancellable/interruptable) and not interact with the threads owned by the pool directly.
Additionally you would not know what job is being executed at the time you try to interrupt the thread, so I can't see why you would be interested in doing this

Update:
The proper way to cancel your task submitted in the thread pool is via the Future for the task returned by the executor.
1)This way you know for sure that the task you actually aim at is attempted to be cancelled
2)If your tasks are already designed to be cancellable then your are half way there
3) Do not use a flag to indicate cancellation but use Thread.currentThread().interrupt() instead

Update:

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

Solution 2

Suggestion: Similarly to/instead of the flags you're using, create a semaphore with 1 permit (new Semaphore(1)) for each task you need to pause/unpause. At the beginning of the task's working cycle put a code like this:

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

This causes the task to acquire a semaphore permit and immediately release it. Now if you want to pause the thread (a button is pressed, for example), call semaphore.acquire() from another thread. Since the semaphore has 0 permits now, your working thread will pause at the beginning of the next cycle and wait until you call semaphore.release() from the other thread.

(The acquire() method throws InterruptedException, if your working thread gets interrupted while waiting. There is another method acquireUninterruptibly(), which also tries to acquire a permit, but doesn't get interrupted.)

Share:
11,814
Ricardo Santos
Author by

Ricardo Santos

Updated on June 04, 2022

Comments

  • Ricardo Santos
    Ricardo Santos almost 2 years

    Is there a way to use ExecutorService to pause/resume a specific thread?

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

    Imagine that I want to stop the thread which as the id=0 (assuming that to each one is assigned an incremental id until the size of the threadpool is reached).

    After a while, by pressing a button let's say, I want to resume that specific thread and leave all the other threads with their current status, which can be paused or resumed.

    I have found on Java documentation a uncompleted version of PausableThreadPoolExecutor. But it doesn't suit what I need because it resume all the threads in the pool.

    If there's no way to do it with the default implementation of the ExecutorService can anyone point me to a Java implementation for this problem?

  • Ricardo Santos
    Ricardo Santos over 11 years
    Can you be more specific and providing me a real implementation or an example? I really appreciated that, cause I'm struggling with this problem quite a long time.
  • Cratylus
    Cratylus over 11 years
    @RicardoSantos:But why don't you make your tasks cancellable?It is not a good idea to interact with threads your code does not own directly
  • Stuart Marks
    Stuart Marks over 11 years
    I agree with the recommendation to make tasks cancellable. The OP wrote "imagine I want to stop thread id=0". This doesn't make much sense, because at any given time you don't know what thread 0 is doing. But it does make sense to talk about stopping some work currently being done, hence the recommendation about canceling tasks.
  • Ricardo Santos
    Ricardo Santos over 11 years
    Actually my threads are using a flag to be cancelled. On my while loop iteration I use that flag until I set it to false. So yes, my Runnables are cancellables. The problem is that, if I set the flag to false and stop the execution, inspecting the threads status I can see that my thread is on a wait status. Which is somehow cool. Now the problem that I'm having is how to resume their work? And please don't reply with "setting the flag to the previous state" because that doesn't work
  • Ricardo Santos
    Ricardo Santos over 11 years
    @user384706 looks exactly with what I was looking for, except on one part. You're notifying all threads which mean that if I have 2 tasks paused (id=1, and id=2) and want to resume task with id=2, you're notifying all threads (id=1 and id=2). Am I correct?
  • Ricardo Santos
    Ricardo Santos over 11 years
    Sure it does the job but sounds more like an hack and it's a better approach than doing nothing. So thanks for answering. I will testing your approach and @user384706 and see which one has better performance.
  • Cratylus
    Cratylus over 11 years
    @RicardoSantos:Each thread is going to execute on its own InterruptableTask and since these are not shared, the lock is private for each thread
  • Cratylus
    Cratylus over 11 years
    @RicardoSantos:I mean o acting as a lock is not shared among threads
  • Cratylus
    Cratylus over 11 years
    @RicardoSantos:The only pitfall here is that if you leave a thread suspended and then try to shutdown the application, the JVM will not shutdown sice the thread is blocked in wait.You should either make the threads deamons, or implement what you need really carefully
  • Ricardo Santos
    Ricardo Santos over 11 years
    @user384706 one more question. On the else statement why are you using that while on suspended flag? The o.wait() from what I saw is blocking so, I think you don't need it. I've been doing some tests with your implementation and everything works fine even without that while. So I think I can close this question and mark your answer as the best one, or at least the one that fits my needs. Once again thanks. Update: Yes. I saw that! I try to leave one thread in suspend mode and call shutdown and the JVM was left running. I spotted that problem as well.
  • Cratylus
    Cratylus over 11 years
    @RicardoSantos:You MUST always call wait inside a while loop to avoid spurious wake-ups docs.oracle.com/javase/6/docs/api/java/lang/Object.html#wait‌​()
  • Ricardo Santos
    Ricardo Santos over 11 years
    Thanks for the advising. I notice that if you call threadPool.shutdownNow(); all the threads that are in wait() will be interrupted. So why doesn't the JVM stops the execution on the call of shutdownNow()?
  • Cratylus
    Cratylus over 11 years
    @RicardoSantos:shutdown closes the threadpool gracefully, leaving current running tasks to be executed and pending tasks are allowed to complete. shutdownNow cancels everything (returning a lists of all tasks not started) and shuts down abruptly
  • second
    second over 4 years
    @Cratylus: The post is quite old, but there are some issues with the code you display here. Catching the interrupt will reset the Threads interrupt flag, so the while loop condition is still true afterwards. shutdownNow does not cancel everything, it only tries do that, but it has no guarantees of shuting down anything. If a task can not be interrupted it will contine running.
  • Nathan Hughes
    Nathan Hughes about 4 years
    If you’re using a pool, then the threads are interchangeable, and don’t map to devices. Best advice is make tasks cancelable and don’t micromanage threads.