Stop an infinite loop in an ExecutorService task

前端 未结 2 700
旧巷少年郎
旧巷少年郎 2020-12-15 12:57
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import          


        
相关标签:
2条回答
  • 2020-12-15 13:28

    Think about using synchronized (this) { this.wait() } instead of sleep inside call(), and then when you set the boolean flag externally (perhaps directly or via a flag() method; with direct access make sure your flag variable is volatile) call task.notifyAll() to wake up the sleeping thread (make sure your task object is a local variable instead of having it anonymous so that you can call methods on it, and make flag a class attribute within Task).

    It'll also be more efficient like that because loops waste cycles needlessly -- the exact mechanism is called a 'guarded block' (http://java.sun.com/docs/books/tutorial/essential/concurrency/guardmeth.html). When you wake up out of the wait, test for the flag variable just to make sure it was set.

    Edit: looked at the original question more closely and created an example using the existing code and principles (there's more than one way to skin a cat :)). Try this -- the loop here exits due to the interrupted status of the current thread, which has been canceled due to timeout:

    package ett;
    import java.util.Arrays;
    import java.util.List;
    import java.util.concurrent.Callable;
    import java.util.concurrent.CancellationException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    import java.util.concurrent.TimeUnit;
    
    class Task implements Callable<String> {
        public String call() throws Exception {
            String s = "initial";
            System.out.println("Started..");
            for (int i=0;;i++) {
                if (i % 2 == 0) {
                    System.out.println("Even");
                }
                Thread.yield();
                if (Thread.interrupted()) break;
            }
            System.out.println("Finished!");
            s = "Done";
    
            return s;
        }
    }
    
    public class ExecutorServiceTest {
    
        public static void main(String[] args) throws Exception {
            ExecutorService executor = Executors.newSingleThreadExecutor();
            List<Future<String>> result = executor.invokeAll(Arrays.asList(new Task()), 1, TimeUnit.SECONDS);
            executor.shutdown();
    
            System.out.println("came here");
    
            for (Future<String> f : result) {
                try {
                    System.out.println(f.get());
                } catch (CancellationException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-15 13:38

    Yes, you can replace flag (or logically &&) with !Thread.currentThread().isInterrupted().

    This way, when the task is canceled, the loop will be terminated.

    The loop would look something like this:

    while(!Thread.currentThread().isInterrupted() && flag) {
      /* Do work. */
    }
    

    Use should be something like this:

    ExecutorService executor = Executors.newSingleThreadExecutor();
    Future<String> task = executor.submit(new Task());
    String str;
    try {
      str = task.get(5, TimeUnit.SECONDS);
    } finally {
      task.cancel(true);
    }
    
    0 讨论(0)
提交回复
热议问题