Как остановить задачу в ScheduledThreadPoolExecutor, когда я думаю, что она завершена
У меня есть ScheduledThreadPoolExecutor, с которым я планирую задачу запускать с фиксированной скоростью. Я хочу, чтобы задача выполнялась с указанной задержкой максимум 10 раз, пока она не "преуспеет". После этого я не хочу, чтобы задача была повторена. Поэтому в основном мне нужно прекратить выполнение запланированной задачи, когда я хочу, чтобы она была остановлена, но не закрывая ScheduledThreadPoolExecutor. Любая идея, как я это сделаю?
Здесь некоторый псевдокод -
public class ScheduledThreadPoolExecutorTest
{
public static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(15); // no multiple instances, just one to serve all requests
class MyTask implements Runnable
{
private int MAX_ATTEMPTS = 10;
public void run()
{
if(++attempt <= MAX_ATTEMPTS)
{
doX();
if(doXSucceeded)
{
//stop retrying the task anymore
}
}
else
{
//couldn't succeed in MAX attempts, don't bother retrying anymore!
}
}
}
public void main(String[] args)
{
executor.scheduleAtFixedRate(new ScheduledThreadPoolExecutorTest().new MyTask(), 0, 5, TimeUnit.SECONDS);
}
}
Ответы
Ответ 1
запустите этот тест, он печатает 1 2 3 4 5
и останавливает
public class ScheduledThreadPoolExecutorTest {
static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(15); // no
static ScheduledFuture<?> t;
static class MyTask implements Runnable {
private int attempt = 1;
public void run() {
System.out.print(attempt + " ");
if (++attempt > 5) {
t.cancel(false);
}
}
}
public static void main(String[] args) {
t = executor.scheduleAtFixedRate(new MyTask(), 0, 1, TimeUnit.SECONDS);
}
}
Ответ 2
Прекрасно отменяется вне потока:
public class ScheduleTest {
@Test
public void testCancel() throws Exception {
final ScheduledThreadPoolExecutor EXECUTOR = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(2);
ScheduledFuture f1 = EXECUTOR.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("Im alive 1");
}
}, 0, 1, TimeUnit.SECONDS);
ScheduledFuture f2 = EXECUTOR.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("Im alive 2");
}
}, 0, 2, TimeUnit.SECONDS);
Thread.sleep(10000);
f1.cancel(true);
System.out.println("f1 cancel");
Thread.sleep(10000);
f2.cancel(false);
System.out.println("f2 cancel");
Thread.sleep(10000);
}
}
Иногда нить не может быть отменена, она обычно решается с помощью volatile boolean isCancelled;
Ответ 3
CountDownLatch
- альтернативный подход. Когда поток завершится, вызовите countDown()
на защелку. Вызывающий поток вызывает latch.await()
, пока все потоки не завершатся. В этот момент наберите ExecutorService.shutdownNow()
, чтобы ваш основной поток не превратился в зомби.
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledThreadPoolExecutorTest {
static int i = 0;
public static void main(String[] args) throws Exception {
final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
final CountDownLatch latch = new CountDownLatch(1);
executor.scheduleAtFixedRate(() -> {
System.out.println(++i);
if (i > 4) {
latch.countDown();
}
}, 0, 100, TimeUnit.MILLISECONDS);
latch.await();
executor.shutdownNow();
}
}