Перемещая задачу в отдельный поток, избегайте дублирования при выполнении задачи и правильном использовании Executors + Future - PullRequest
0 голосов
/ 01 февраля 2019

У нас есть связующий компонент между устаревшим кодом и текущим кодом.По сути, все унаследованное приложение является однопоточным и имеет ужасные проблемы, когда обновление пользовательского интерфейса для одной инструкции может происходить от 5 до 8 раз.

Я хочу опубликовать асинхронное сообщение после того, как первый запрос на обновление произойдет +2 секунды.

Давайте не будем зацикливаться на том, почему, это не то, что я действительно хочу сделать, но у меня естьчтобы понять, как по крайней мере сделать это, чтобы я мог реализовать реальное решение.

Runnable task = () -> {
    try {
        TimeUnit.SECONDS.sleep(2);
        messageBus.publishAsynch(new LegacyUiUpdateEvent());
    } catch (InterruptedException e) {
        // TODO Log something
        Thread.currentThread().interrupt();
    }
};

@Override
public void update(Observable arg0, Object arg1) {
    ExecutorService executor = Executors.newSingleThreadExecutor();
    if (futureTask == null || futureTask.isDone()) {
        futureTask = executor.submit(task);
        try {
            executor.awaitTermination(10, TimeUnit.SECONDS);
            executor.shutdownNow();
        } catch (InterruptedException e) {
            // TODO Log something
            Thread.currentThread().interrupt();
        }
    }
}

Теория: Если будущая задача не существует, мы создаем ее, как только она там, если это не сделано (потому что это ложное устаревшее обновление 4 / x, где x ∈ [5,12] и спящий режим все еще действует), то мы полностью пропускаем и не создаем нового исполнителя.

Проблема в том, что , насколько я могу судить, executor.submit(task) на самом деле не происходит на новом протекторе.Как я уже говорил, унаследованное приложение является однопоточным, и после того, как я увеличил сон до 15 секунд, было совершенно очевидно, что оно отправляет весь текущий поток в спящий режим.

Как бы я поместил свои такты в совершенно новый поток(используя библиотеку concurrency) и избегая выполнения задачи несколько раз, даже если метод обновления вызывается слишком часто (и это на 100% вне моего контроля).Я думаю, что future.isDone() вещь работает, но не 100%

Ответы [ 2 ]

0 голосов
/ 01 февраля 2019

если вы работаете на Java 8 или выше, лучше сделать

CompletableFuture.runAsync(task);

, потому что это будет выполняться в пуле потоков Fork-join, который управляется JVM, и вы будетене заботьтесь о себе, создавая или закрывая его.и, конечно, это будет выполняться асинхронно, что соответствует вашим требованиям.

0 голосов
/ 01 февраля 2019

executor.submit() запускает задачу в новом потоке, но сразу после того, как executor.awaitTermination(10, TimeUnit.SECONDS); ожидает в потоке current , чтобы задача была завершена.Нет необходимости ждать в потоке current , но должен быть способ определить, выполняется ли уже задание.

Беспорядочная часть создает ExecutorService каждый раз- нет необходимости каждый раз воссоздавать его.Это может быть переменная экземпляра класса и использоваться повторно.В идеале, он должен быть внедрен через конструктор, чтобы класс, который его создал, мог выключить его , если это действительно необходимо .

 private final ExecutorService executor = Executors.newSingleThreadExecutor();  // or injected through constructor
 private Future<?> futureTask;

@Override
public void update(Observable arg0, Object arg1) {
    if (futureTask == null || futureTask.isDone()) {
        futureTask = executor.submit(task);
    }
}
...