элегантный способ изящной остановки очереди с периодическими потребителями по таймауту - PullRequest
0 голосов
/ 25 апреля 2011

У меня есть очередь с несколькими производителями - один потребитель.Потребитель периодически запускается и полностью выгружает очередь (после нее не осталось сообщений).Изящный алгоритм должен запустить потребителя и ждать его с таймаутом или просто ждать, если потребитель уже работает.

В настоящее время мы имеем что-то вроде этого:

void stop(boolean graceful) {
        if (graceful && !checkAndStopDirectly()) {
            executor.shutdown();
            try {
                if (!executor.awaitTermination(shutdownWaitInterval, shutdownWaitIntervalUnit)) {
                    log.warn("...");
                }
            } catch (InterruptedException e) {
                log.error("...", e);
            }
        } else {
            executor.shutdownNow();
        }

private boolean checkAndStopDirectly() {
    ExecutorService shutdownExecutor = Executors.newSingleThreadExecutor();
    try {
        return shutdownExecutor.submit(new Callable<Boolean>(){
            @Override
            public Boolean call() throws Exception {
                if (isAlreadyRan.compareAndSet(false, true)) {
                    try {
                        runnableDrainTask.run();
                    } finally {
                        isAlreadyRan.set(false);
                    }
                    return true;
                } else {
                    return false;
                }
            }
        }).get(shutdownWaitInterval, shutdownWaitIntervalUnit);

Кто-нибудь видит более элегантнымспособ сделать это?например, я ищу способ без использования дополнительного AtomicBoolean (isAlreadyRan) или двойной логики ожидания с временными интервалами в качестве полей объекта и т. д. Кстати, шаблон ядовитой таблетки приходит мне в голову ...

1 Ответ

1 голос
/ 06 сентября 2011

Вы говорите здесь о грациозном завершении работы вашего приложения, должно делать следующее?

  1. дождитесь окончания очереди или
  2. тайм-аут, если сток занимает слишком много времени

Мне может понадобиться понять, как вы истощаете очередь, но если вы можете сделать это таким образом, чтобы она была прерываемой, вы можете установить тайм-аут при получении Future и попробовать shutdownNow ( прерывая, если не полностью истощена) независимо; как это выглядит для вас?

ExecutorService pool = Executors.newSingleThreadExecutor();

public void stop() {
    try {
        pool.submit(new DrainTask()).get(100, MILLISECONDS);
    } catch (TimeoutException e) {
        // nada, the timeout indicates the queue hasn't drained yet
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    } catch (Exception e) {
        // nada, defer to finally
    } finally {
        pool.shutdownNow();
    }
}

private class DrainTask implements Callable<Void> {
    @Override
    public Void call() throws Exception {
        while (!Thread.currentThread().isInterrupted())
            ; // drain away
        return null;
    }
}

Используется ли деталь compareAndSet для защиты от нескольких одновременных вызовов на stop? Я думаю, что предпочел бы общий замок или использовал бы вместо него synchronized. Однако в случае конфликта выдается ExecutionException, и повторные вызовы на shutdownNow подойдут.

Это отчасти зависит от способности DrainTask останавливать то, что он делает в ответ на вызов прерывания (так как shutdownNow будет пытаться вызвать прерывание на любых работающих в данный момент потоках).

...