Проблемы присвоения Java - действительно ли это является атомарным?

У меня есть некоторые вопросы о присвоении Java.

  • Строки

У меня есть класс:

public class Test {
 private String s;

 public synchronized void setS(String str){
  s = s + " - " + str;
 }

 public String getS(){
  return s;
 }
}

Я использую, "синхронизировался" в моем методе set и предотвращении его в моем методе считывания, потому что в моем приложении, существует тонны получения данных и очень немногих настроек. Настройки должны синхронизироваться для предотвращения несоответствия. Мой вопрос: действительно ли получение и установка являются атомарной переменной? Я имею в виду в многопоточной среде, Thread1 собирается установить переменную s, в то время как Thread2 собирается получить "s". Действительно ли там каким-либо путем является метод получателя, мог получить что-то другое, чем старое значение s, или новое значение s (предположите, что у нас есть только два потока)? В моем приложении это не проблема для получения нового значения, и это не проблема для получения старой. Но я мог получить что-то еще?

  • Что относительно получения и помещения HashMap?

рассмотрение этого:

    public class Test {
        private Map<Integer, String> map = Collections.synchronizedMap(new HashMap<Integer, String>());

        public synchronized void setMapElement(Integer key, String value){
         map.put(key, value);
        }

        public String getValue(Integer key){
         return map.get(key);
        }
}

Действительно ли помещение и получение являются атомарными? Как HashMap обрабатывает помещение элемента в него? Это сначала удаляет старое значение и помещать теперь одно? Я мог добраться кроме старого значения или нового значения?

Заранее спасибо!

9
задан Bob 26 March 2010 в 18:05
поделиться

5 ответов

В первом случае String оказывается безопасным для небезопасной публикации (в «новой» модели памяти Java (JMM)), так что это нормально.

Не будучи изменчивым , теоретически существует некоторая проблема, связанная с отсутствием актуального значения, но тогда значение актуального значения неясно. Вы можете заменить блокировку циклом сравнения-песка-замены (CAS), но это, вероятно, не даст вам большого прироста производительности независимо от того, будет ли блокировка оспорена или нет.

В случае HashMap несинхронизированное отображение небезопасно для чтения, если в него записывает другой поток, даже один поток записи. Фактически, было обнаружено, что это приводит к бесконечным циклам в производственных системах, на которых работает популярное программное обеспечение. Код в вопросе фактически использует две блокировки для карты, которая находится сверху (хотя вам понадобится явное удержание той же блокировки при использовании итератора). Отсутствие final действительно мешает содержащемуся классу быть безопасным для небезопасной публикации. Если map был volatile и вы создавали новую карту для каждого put , то это можно было бы сделать безопасным без синхронизации при получении.

6
ответ дан 4 December 2019 в 11:41
поделиться

Вместо того, чтобы оборачивать HashMap чем-то для синхронизации, рассмотрите возможность использования java.util.concurrency.ConcurrentHashMap .

Это обновленная версия HashMap, которая гарантирует, что «извлечения отражают результаты последних завершенных операций обновления, удерживаемых с момента их начала.».

6
ответ дан 4 December 2019 в 11:41
поделиться

Может быть, Блокировка чтения-записи может решить вашу проблему?

Взгляните на ее документацию:

Блокировка чтения-записи обеспечивает более высокий уровень параллелизма при доступе к общим данным, чем этот разрешено блокировкой взаимного исключения. Он использует тот факт, что, хотя только один поток одновременно (поток записи) может изменять общие данные, во многих случаях любое количество потоков может одновременно читать данные (следовательно, потоки чтения). Теоретически увеличение параллелизма, разрешенное использованием блокировки чтения-записи, приведет к повышению производительности по сравнению с использованием блокировки взаимного исключения. ....

2
ответ дан 4 December 2019 в 11:41
поделиться

В многопоточной среде вам потребуется синхронизировать получатель, чтобы гарантировать, что клиент видит самое последнее значение s .

1
ответ дан 4 December 2019 в 11:41
поделиться

Более ранние ответы верны, указывая, что с новыми (1.5+) JVM версия String безопасна в отношении повреждения данных. И вы, кажется, знаете о недостатках несинхронизированного доступа; изменения, не обязательно видимые через геттеры.

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

3
ответ дан 4 December 2019 в 11:41
поделиться
Другие вопросы по тегам:

Похожие вопросы: