Java одновременный доступ к полю, трюк, чтобы не использовать volatile - PullRequest
0 голосов
/ 26 марта 2012

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

У меня есть поле, List<Something>, которое заполняется после построения. Чтобы сохранить производительность, я хотел бы преобразовать Список в карту только для чтения. Для этого в любой момент требуется как минимум изменчивое поле карты, поэтому внесите изменения, видимые для всех потоков.

Я думал сделать следующее:

Map map;

public void get(Object key){
    if(map==null){
        Map temp = new Map();
        for(Object value : super.getList()){
            temp.put(value.getKey(),value);
        }
        map = temp;
    }
     return map.get(key);
}

Это может привести к тому, что несколько потоков сгенерируют карту, даже если они войдут в блок get сериализованным способом. Это не будет большой проблемой, если потоки работают на разных идентичных экземплярах карты. Что меня беспокоит больше всего:

Возможно ли, что один поток назначит новую временную карту полю карты, а затем второй поток увидит это map!=null и, следовательно, получит доступ к полю карты, не создавая новое, но, к моему удивлению, обнаружит, что карта пусто, потому что операции put, которые еще не помещены в какую-либо область общей памяти?

Ответы на комментарии:

  • Потоки изменяют временную карту только после того, как она доступна только для чтения.
  • Я должен преобразовать Список в Карту из-за какой-то особой настройки JAXB, которая не дает возможности иметь Карту для начала.

Ответы [ 4 ]

3 голосов
/ 26 марта 2012

Возможно ли, что один поток назначает новую временную карту полю карты, а затем второй поток видит это map!=null и, следовательно, обращается к полю карты, не генерируя новое, но, к моему удивлению, обнаруживает, чтокарта пуста, потому что операции put не были перенесены в область общей памяти?

Да, это абсолютно возможно;например, оптимизирующий компилятор может фактически полностью избавиться от локальной переменной temp и просто использовать поле map все время, при условии, что оно восстановит map до null в случае исключения.

Аналогично, поток также может видеть ненулевое, непустое map, которое, тем не менее, не заполнено полностью.И если ваш класс Map не предназначен для одновременного чтения и записи (или использует synchronized, чтобы избежать проблемы), вы также можете получить странное поведение, если один поток вызывает свой метод get, а другой вызывает его put.

1 голос
/ 26 марта 2012

Можете ли вы создать свою карту в ctor и объявить ее окончательной?При условии, что вы не утечете карту, чтобы другие могли ее изменить, этого должно быть достаточно, чтобы ваш get () был безопасно разделен несколькими потоками.

0 голосов
/ 06 июня 2016

Помимо упомянутых ранее проблем с видимостью, существует еще одна проблема с исходным кодом, а именно:он может генерировать исключение NullPointerException здесь:

return this.map.get(key)

Что нелогично, но это то, что вы можете ожидать от неправильно синхронизированного кода.

Пример кода для предотвращения этого:

Map temp;
if ((temp = this.map) == null)
{

    temp = new ImmutableMap(getList());
    this.map = temp;
}
return temp.get(key);
0 голосов
/ 26 марта 2012

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

карта является нулевой или полной

static class MyMap extends HashMap {
   MyMap (List pList) {
    for(Object value : pList){
        put(value.getKey(), value);
    }
   }
}

MyMap map;

public Object get(Object key){
    if(map==null){
        map = new MyMap (super.getList());
    }
    return map.get(key);
 }

Или кто-то видит новую введенную проблему?

...