Проблемы с назначением Java - это атомарно? - PullRequest
9 голосов
/ 26 марта 2010

У меня есть несколько вопросов о назначении Java.

  • Строка

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

public class Test {
 private String s;

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

 public String getS(){
  return s;
 }
}

Я использую «синхронизированный» в своем установщике и избегаю его в своем установщике, потому что в моем приложении есть тонны получения данных и очень мало настроек. Настройки должны быть синхронизированы, чтобы избежать несоответствия. Мой вопрос: получение и установка атомарной переменной? Я имею в виду, что в многопоточной среде 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 обрабатывает помещение элемента в него? Сначала он удаляет старое значение и ставит сейчас? Могу ли я получить что-то кроме старого или нового значения?

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

Ответы [ 5 ]

6 голосов
/ 26 марта 2010

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

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

6 голосов
/ 26 марта 2010

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

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

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

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

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

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

2 голосов
/ 26 марта 2010

Возможно, Read Write Lock может решить вашу проблему?

Посмотрите документацию:

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

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

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

...