Параллельный доступ к неизменяемой карте

@Singleton
@LocalBean
@Startup
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class DeliverersHolderSingleton {

    private volatile Map<String, Deliverer> deliverers;

    @PostConstruct
    private void init() {
        Map<String, Deliverer> deliverersMod = new HashMap<>();
        for (String delivererName : delivererNames) {
            /*gettig deliverer by name*/
            deliverersMod.put(delivererName, deliverer);
        }
        deliverers = Collections.unmodifiableMap(deliverersMod);
    }

    public Deliverer getDeliverer(String delivererName) {
        return deliverers.get(delivererName);
    }

    @Schedule(minute="*", hour="*")
    public void maintenance() {
        init();
    }
}

Синглтон используется для хранения данных. Данные обновляются раз в минуту. Возможно ли, что чтение из немодифицируемой карты будет проблемой с синхронизацией? Возможно ли, что произойдет переупорядочение в методе init, и ссылка на коллекцию будет опубликована, но коллекция заполнена не полностью?

3 ответа

Решение

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

Запись в энергозависимое поле (§8.3.1.4) происходит перед каждым последующим чтением этого поля.

Это идет дальше и гарантирует, что любая операция, которая произошла до операции записи, также будет видна в точке чтения (благодаря правилу упорядочения программы и тому факту, что отношение " происходит до" является транзитивным).

Ваш getDeliverers Метод читает из переменной volatile, поэтому он будет видеть последнюю запись, выполненную в строке deliverers = Collections.unmodifiableMap(deliverersMod); а также предыдущие операции, когда карта заполнена.

Таким образом, ваш код является потокобезопасным и ваш getDeliverers Метод вернет результат, основанный на последней версии вашей карты.

В соответствии с сеткой переупорядочения, найденной здесь http://g.oswego.edu/dl/jmm/cookbook.html, первая операция Normal Store не может быть переупорядочен с помощью второй операции Volatile StoreТаким образом, в вашем случае, если неизменяемая карта не равна нулю, проблем с переупорядочением не будет.

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

Вопросы безопасности темы здесь:

  • многократное чтение из HashMap - потокобезопасно, потому что допускается многократное чтение, если нет изменений в коллекции и записи в HashMap не произойдет, потому что карта является unmodifiableMap()

  • читать / писать на deliverers - потокобезопасен, потому что все ссылки на Java являются атомарными

Я не вижу здесь операций, небезопасных для потоков.

Я хотел бы отметить, что имя init() метод вводит в заблуждение, он предполагает, что он вызывается один раз во время инициализации; Я бы предложил позвонить rebuild() или же recreate(),

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