Нужно ли синхронизировать RPC в этом сценарии?

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

Давайте возьмем следующий сценарий: у меня есть представление верхнего уровня, которое передает список данных List<SomeObject> в свои подчиненные представления. Все докладчики подуровня будут работать с этим списком данных, добавляя, удаляя и изменяя элементы этого списка.

Допустим, пользователь делает что-то в пользовательском интерфейсе, а затем нажимает кнопку «Сохранить» на нескольких экземплярах представления подуровня, которые вызывают SubLevelPresenter.onSaveSomeObjectButtonClick(). Это выполнит вызов RPC, результат которого будет добавлен в список List<Somebject>.

Я предполагаю, что возможно, что если пользователь нажмет две разные кнопки сохранения, данные могут поступить в одно и то же время, одновременно вызывая onSaveSomeObjectButtonClick(), что приведет к одновременному доступу к List<SomeObject>.

Должен ли я синхронизировать эти действия, реализуя очередь, например. или я в безопасности из-за какой-то внутренней магии GWT/JavaScript, которую я здесь не вижу?

public class TopLevelPresenter {

    private List<SomeObject> someObjectList = new ArrayList<>();    
    private List<SubLevelPresenter> cache = new ArrayList<>();

    public void TopLevelPresenter(TopLevelModel topLevelModel, TopLevelView topLevelView) {

        this.topLevelModel = topLevelModel;
        this.topLevelView = topLevelView;

        for(int i = 0; i < topLevelModel.getNumOfSubViews(); i++) {

            // Pass the data
            SubLevelModel subLevelModel = new SubLevelModel(someObjectList);
            SubLevelView subLevelView = new SubLevelView();         
            SubLevelPresenter subLevelPresenter = new SubLevelPresenter(subLevelModel, subLevelView);
            cache.add(subLevelPresenter);
        }
    }
}

public class SubLevelModel() {

    private List<SomeObject> someObjectList;

    public SubLevelModel(List<SomeObject> someObjectList) {
        this.someObjectList = someObjectList;
    }

    public void addSomeObject(SomeObject someObject) {
        this.someObjectList.add(someObject);
    }

    public void removeSomeObject(SomeObject someObject) {
        this.someObjectList.remove(someObject);
    }
}

public class SubLevelPresenter() {

    private SomeServiceAsync someService = /* .. */;
    private SubLevelView subLevelView;  
    private SubLevelModel subLevelModel;

    public SubLevelPresenter(SubLevelModel subLevelModel, SubLevelView subLevelView) {
        this.subLevelView = subLevelView;
        this.subLevelModel = subLevelModel;
    }

    public void onSaveSomeObjectButtonClick() {

        SomeObject toSave = this.subLevelView.getSuggestionBox().getSelection();

        someService.saveSomeObject(toSave, new AsyncCallback<SomeObject>() {

            @Override
            public void onFailure(Throwable caught) {
                Window.alert("RPC to saveSomeObject() failed.");
            }

            @Override
            public void onSuccess(SomeObject savedObject) {
                SubLevelPresenter.this.subLevelModel.addSomeObject(savedObject);
            }
        });
    }
}

person Stefan Falk    schedule 25.08.2015    source источник


Ответы (2)


Ключевое слово synchronized бесполезно (и игнорируется GWT) в коде на стороне клиента, учитывая, что JavaScript является однопоточным. Даже если ответы RPC поступят в одно и то же время, они будут поставлены в очередь в цикле событий JavaScript и обработаны один за другим.

Обратите внимание, что в браузерах (а именно Firefox) есть ошибки, приводящие к состязаниям, но их нельзя обнаружить и/или обойти так же просто, как удалить в коде ключевое слово synchronized (или эквивалентное ему). Также обратите внимание на ошибку Firefox, связанную с alert() и confirm().

person Thomas Broyer    schedule 25.08.2015
comment
Ладно, я так и думал, но не был уверен. Итак, допустим, я действительно боюсь состояния гонки, имеет ли смысл построить очередь, которая отправляет один запрос за другим? Кажется, что это может быть просто обходной путь для Firefox. - person Stefan Falk; 25.08.2015
comment
Это просто перенесет проблему в управление очередью. И добавим в баланс: вероятность и частота 2 запросов в полете, вероятность состояния гонки в глючном браузере (обратите внимание, мы не знаем, существует ли такой браузер, Проблема с Firefox, по-видимому, связана с блокировкой alert() и confirm(), поэтому зависит от того, используете ли вы какой-либо из них), влияние на ваш код (включая ремонтопригодность), влияние на производительность (и воспринимаемую производительность для пользователя), последствия, если это произойдет (исправление для пользователя, если он заметит, что осталось всего одно обновление) - person Thomas Broyer; 25.08.2015
comment
Хорошо, понял :) Спасибо! - person Stefan Falk; 25.08.2015

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

Collections.synchronizedList(new ArrayList<String>());
person ArcTanH    schedule 25.08.2015
comment
…или CopyOnWriteArrayList. Но это относится только к коду, который выполняется в JVM, а не к коду, скомпилированному в JavaScript с использованием GWT, потому что JavaScript. - person Thomas Broyer; 25.08.2015