Параллелизм Java: поведение CopyOnWriteArrayList

Мне нужно сохранить некоторые объекты в базе данных.

Прежде всего,

  1. Храню их в памяти (в коллекцию)
  2. Когда один из них правильно хранится в базе данных, я удаляю его.

Так,

public class AuditService {
    private CopyOnWriteArrayList<Audit> copyWrite;

    public void flush(Audit... audits) {
        Collection<Audit> auditCollection = Arrays.asList(audits);
        this.copyWrite.addAll(auditCollection);

        this.copyWrite.forEach(audit -> {
            // save audit object on database
            this.copyWrite.remove(audit);

        });
    }
}

Я должен быть потокобезопасным, я имею в виду, что AuditService - это одноэлементный класс, я могу одновременно использовать несколько потоков в методе flush.

У меня вопрос:

  1. Как именно работает CopyOnWriteArrayList, чтобы решить проблему параллелизма.
  2. Это правильный код?
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
0
38
1

Ответы 1

CopyOnWriteArrayList обеспечивает безопасность потоков, копируя базовый массив при изменении данных. Операции мутатора, такие как addAll() в вашем примере, внутренне синхронизируются CopyOnWriteArrayList.

Однако ваш код не имеет особого смысла, поскольку доступ к полю copyWrite не осуществляется вне метода flush(). Переменные локального метода являются потокобезопасными, поэтому ваш код можно упростить до простого:

public void flush(Audit... audits) {
    for (Audit a : audits) {
        // save audit object on database
    }
}

Проблема в том, что произойдет, если объект Audit будет изменен. Надеюсь, вы сделали их неизменяемыми, поскольку нет смысла изменять события Audit.

Аудиты сначала сохраняются на CopyOnWriteArrayList, поскольку в случае отказа save audit on database operation в следующий раз, когда будет достигнут flush, ожидающие аудиты будут снова попытки сохранения. О чем вы думаете.

Jordi 11.12.2018 12:32

В этом мало смысла, если вы не ожидаете, что сервер базы данных выйдет из строя больше, чем сервер приложений. Обычно все наоборот. Возможно, я не понимаю вашего пейзажа.

Karol Dowbecki 11.12.2018 12:35

Вы предлагаете мне другую стратегию?

Jordi 11.12.2018 12:35

Возможно ли, чтобы один и тот же объект аудита, хранящийся в CopyOnWriteArrayList, дважды сохранялся в базе данных?

Jordi 11.12.2018 12:37

Я имею в виду, что происходит, когда два потока достигают forEach? Собираются ли оба потока «перечислять» одни и те же аудиты, и тогда один и тот же аудит может быть сохранен дважды ...?

Jordi 11.12.2018 12:41

@Jordi, в этом случае вы, скорее всего, захотите использовать ограничения базы данных для дедупликации. В вашем текущем подходе Java нет логики деэпультации, и он не будет работать, если вы развернете свое приложение дважды и у вас будет более одной JVM. Вероятно, вам следует задать здесь новый вопрос, он не имеет ничего общего с CopyOnWriteArrayList.

Karol Dowbecki 11.12.2018 12:45

Другие вопросы по теме