Определение полей в параллельной среде - PullRequest
4 голосов
/ 26 марта 2019

В моем классе есть два метода, которые будут выполняться в параллельной среде:

class Clazz {

  private int counter = 0;
  private volatile Map<..> map = new ConcurrentHashMap<>();
  private int[] array;

  public void concurrentMethod() {
    ...perform some actions with map...
  }

  public int nonConcurrentMethod() {
    ...reinitialize the map...change references...
    counter++;
    return array[counter];
  }

}

Вопрос заключается в следующем: если предположить, что nonConcurrentMethod должен вызываться только одним потоком за раз, я должен явно указать counter и array как поля volatile? Сделать счетчик atomic?

Я думаю, что было бы лучше, чтобы все работало без сбоев на реальном производстве.

Ответы [ 3 ]

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

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

IMO, если вы не можете разделить Clazz на два отдельных класса с различной семантикой, ориентированной на многопотоковое исполнение, было бы разумно сделать nonConcurrentMethod() поточно-безопасным. В случае, если nonConcurrentMetho() вызывается из нескольких потоков, производительность будет ухудшаться, но правильность будет сохранена, надеясь избежать трудностей при поиске ошибок.

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

private final Object lock = new Object();

public int nonConcurrentMethod() {
  synchronized(lock) {
    ...reinitialize the map...change references...
    counter++;
    return array[counter];
  }
}

Убедитесь, что хотя бы одно из полей Clazz имеет значение final, чтобы обеспечить безопасную публикацию .

0 голосов
/ 26 марта 2019

Лучше перемещать общие объекты в другой класс, который доступен нескольким потокам.

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

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


Даже все методы в ConcurrentHashMap являются поточно-ориентированными, но мы должны позаботиться о том, чтобы обеспечить безопасность потоков, когда разные многопоточные методы вызываются несколькими потоками.

Например:

Map map = new ConcurrentHashMap();

Thread t1 = new Thread();
Thread t2 = new Thread();

public void putIfAbsent(String key, String value) {
    if (!map.containsKey(key)) {
        map.put(key, value);
    }
}

В приведенном выше примере у нас есть общий ресурс map, и два потока помещают некоторые значения, если они отсутствуют.

Чтобы избежать такой ситуации, мы можем использовать две техники, как указано ниже;

  • Либо каждый вызывающий абонент обязательно сделает его безопасным для потоков (на стороне клиента)
  • Или определить новый метод в одном месте для обеспечения безопасности потока (на стороне сервера Предпочтительнее )
0 голосов
/ 26 марта 2019

Из информации, которую вы предоставили отдельным изменениям для map, будут видеть пользователи concurrentMethod, но изменения, сделанные внутри вызова метода, не изолированы от другого вызова метода любого другого метода. Следовательно, если nonConcurrentMethod в какой-то момент clear() s на карте, concurrentMethod может видеть, что записи исчезают из map между вызовами методов на карте.

Например:

concurrentMethod() {
    System.out.println(map.size());
    System.out.println(map.size());
}

Может выдать:

10
0

Если nonConcurrentMethod очищает карту и вызывается во время работы concurrentMethod.

Если вы хотите обеспечить атомарный доступ, тогда вместо использования ConcurrentHashMap попробуйте использовать обычный HashMap и защитить его, защищая методы с помощью ключевого слова synchronized или используя более детальную блокировку, либо через synchronized(lock) { } или с помощью некоторых инструментов блокировки, предоставляемых в пакете параллелизма Java, таких как интерфейс ReadWriteLock.

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