Согласованная синхронизация Java - PullRequest
1 голос
/ 11 января 2011

Мы сталкиваемся со следующей проблемой в службе Spring в многопоточной среде:

  • три списка свободно и независимо доступны для чтения
  • время от времени (каждые 5 минут) все они обновляются до новых значений.Между списками есть некоторые зависимости, из-за которых, например, третий не следует читать, пока второй обновляется, а первый уже имеет новые значения;это нарушило бы согласованность трех списков.

Моя первоначальная идея - создать контейнерный объект, имеющий три списка в качестве свойств.Затем синхронизация будет сначала для этого объекта, а затем по одному в каждом из трех списков.

Какой-то код стоит тысячи слов ... поэтому вот черновик

    private class Sync {
        final List<Something> a = Collections.synchronizedList(new ArrayList<Something>());
        final List<Something> b = Collections.synchronizedList(new ArrayList<Something>());
        final List<Something> c = Collections.synchronizedList(new ArrayList<Something>());
    }

    private Sync _sync = new Sync();

   ...

   void updateRunOnceEveryFiveMinutes() {
     final List<Something> newa = new ArrayList<Something>();
     final List<Something> newb = new ArrayList<Something>();
     final List<Something> newc = new ArrayList<Something>();

     ...building newa, newb and newc...

     synchronized(_sync) {

        synchronized(_sync.a) {
            _synch.a.clear();
            _synch.a.addAll(newa);
        }

        synchronized(_sync.b) { ...same with newb... }

        synchronized(_sync.c) { ...same with newc... }
   }

   // Next is accessed by clients

   public List<Something> getListA() {
      return _sync.a;
   }

   public List<Something> getListB() { ...same with b... }

   public List<Something> getListC() { ...same with c... }

Вопрос будет,

  • этот черновик безопасен (неттупик, согласованность данных)?
  • не могли бы вы предложить более эффективную реализацию для этой конкретной проблемы?

update

Изменен порядокСинхронизация _sync и newa ... building.

Спасибо

Ответы [ 3 ]

2 голосов
/ 11 января 2011

ваш лучший вариант - выставить объект "текущее состояние" и использовать volatile, отключив весь синхронизированный код.(также вы можете захотеть сделать списки неизменяемыми упаковщиками просто для безопасности).

public class ListState {
  List final a;
  List final b;
  List final c;
}

private volatile ListState _state;

void updateRunOnceEveryFiveMinutes() {
  // generate new lists ...

  // re-assign current list state (e.g. atomically publish all your updates)
  _state = new ListState(newA, newB, newC);
}

public ListState getCurrentLists() {
  return _state;
}
1 голос
/ 11 января 2011

Черновик не достигает вашей цели.Здесь нет ничего, что препятствует доступу к старому C, пока вычисляются новые A или B.Самый простой способ сделать это примерно так:

synchronized (a) {
  synchronized (b) {
    synchronized (c) {
    }
  }
}

вокруг всех обращений, будь то чтение или запись.И я бы не стал делать clear () и addAll (), просто изменил бы значение переменной на newA / newB / newC, хотя это потребовало бы некоторой корректировки вышеуказанного.

1 голос
/ 11 января 2011

Построить новый список немного расточительно, только чтобы скопировать его в другой (окончательный) список.

Попробуйте использовать неизменяемые списки и назначить новый список _sync.a, _sync.b и _sync.c (удалив из них модификатор final) после построения списка (в updateRunOnceEveryFiveMinutes.)

Также вы синхронизируете на 4 разных объектах. Было бы проще синхронизировать только один объект (_sync или, возможно, выделенный объект блокировки) для каждого вызова метода.

Если списки, возвращаемые getListA и т. Д., Являются неизменяемыми, вам также не нужны вызовы Collections.synchronizedList.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...