Флаг остановки в многопоточном обработчике

День добрый.
Не смотря на то, что давольно давно пишу на Java, специалистом в области многопоточного программирования не являюсь. Поэтому хочу проконсультироваться у более опытных коллег.
Задача:
Есть некий класс, в многопоточном режиме обрабатывающий запросы (бизнес метод service). Нужно сделать метод остановки (назовем его destroy). Работать должно следующим образом:
Нужно сделать так, что бы после вызова метода destroy новые запросы не обрабатывались, обработка старых благополучно завершилась и после завершения последней обработки выполнились какие-то действия.
Я сделал так:
public class Worker{
    AtomicBoolean flag;
    AtomicInteger counter;

    public void destroy() {
	flag.set(true);
	if (counter.get() == 0) {
	    destroyImpl();
	}
    }

    public void service(String arg) {
	if (!flag.get()) {
	    counter.incrementAndGet();
	    if (!flag.get()) {
		serviceImpl(arg);
	    }
	    counter.decrementAndGet();
	} else {
	    if (counter.get() == 0) {
		destroyImpl();
	    }
	}
    }

}

Подозреваю что не только коряво, но и глючно.
Как правильно?
  • Вопрос задан
  • 3086 просмотров
Решения вопроса 1
Совершенно неправильное использование атомарных операций. Метод destroyImpl(), например, может быть вызван многократно. Почему бы вам не воспользоваться классическими блокировками и не избавиться от атомарных сущностей? Так проще и намного читабельней. К тому же вам необходимо работать сразу с несколькими полями — а это нетривиально, если использовать лишь атомарные операции.

Должно получиться что-то вроде этого:
public class Worker {

    private boolean destroy;
    private boolean destroyed;
    private counter;

    public void destroy() {
        synchronized (this) {
            if (this.destroyed) {
                return;
            }
            this.destroy = true;
            if (this.counter != 0) {
                return;
            }
            this.destroyed = true;
        }
        destroyImpl();
    }

    public void service(String arg) {
        synchronized (this) {
            if (this.destroy) {
                return;
            }
            ++this.counter;
        }
        serviceImpl(arg);
        synchronized (this) {
            if (--this.counter != 0) {
                return;
            }
            if (!this.destroy) {
                return;
            }
            this.destroyed = true;
        }
        destroyImpl()
    }

}
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
FanKiLL
@FanKiLL
Используйие ThreadPoolExecutor У него есть метод shutdown() который после вызова закончит «таски», которые вы ему дали на выполнение, но новые принимать больше не будет.

Таски могут быть к примеру или Callable<T> если хотите возвращать значение или Runnable
Ответ написан
Divers
@Divers
Несколько раз прочитал вопрос, но так и не понял — у вас 1 инстанс Worker на много потоков? Если на нем синхронизироваться, то вы же получите обычное последовательное исполнение, зачем тогда вообще многопоточность. Поясните пожалуйста.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы