Заполнение коллекции или карты из нескольких методов в классе с использованием потокобезопасного подхода

#java #multithreading #spring-boot #spring-batch #java.util.concurrent

#java #многопоточность #весенняя загрузка #spring-batch #java.util.concurrent

Вопрос:

Пожалуйста, посоветуйте наилучший подход для решения следующих задач:

У меня есть класс Java следующим образом:

 @Service
public class ServiceAImpl {

    private final Set<String> someSet = new HashSet<>();
    private final List<Record> someList = new ArrayList<>();
    private final Map<String, String> someMap = new HashMap<>();

   //Loads XML as a document
   public Optional<Document> readXML(Document xmlDoc){

   }

   //reads the root node and triggers the recursive extraction of the XML data
   public List<Record> extractRootNodeAndTriggersRecursive(){
       extractXMLRecursively(elt);

   }

   //recursively read hierarchical data in the XML
   public SomeObject extractXMLRecursively(Element elt){

   }

   public SomeObject createReportFromTheMapOrList(){

   }

}
 

Я знаю, что члены экземпляра (или список / карты) не являются потокобезопасными.

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

Итак, в другом вызывающем классе (основное приложение Springboot) я буду получать список / набор / карту для их обработки после заполнения.

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

Кто-нибудь сталкивался с этим сценарием?

Любые идеи, советы или ссылки будут оценены.

Спасибо.

Комментарии:

1. Вы можете использовать потокобезопасные коллекции и карты, т. Е. CopyOnWriteArrayList Если у вас мало записей, или List<Record> someList = Collections.synchronizedList(new ArrayList<>()) . Для карт вы можете использовать ConcurrentHashMap , а для наборов существует ConcurrentSkipListSet или вы также можете использовать Map<String, String> someMap = Collections.newSetFromMap(new ConcurrentHashMap<>())

2. спасибо fps. Как это соотносится с использованием ReentrantLock в соответствии с ответом, предложенным нахаевым?

3. Блокировки позволяют синхронизировать доступ к нескольким коллекциям одновременно, т.Е. Добавлять элементы в три коллекции и выполнять эту операцию атомарно

Ответ №1:

Вы можете использовать ReentrantLock :

 import java.util.concurrent.locks.ReentrantLock;

@Service
public class ServiceAImpl {

    private final Set<String> setNoDuplicateLabelsAllowed = new HashSet<>();
    private final Set<String> someSet = new HashSet<>();

    private final ReentrantLock lock = new ReentrantLock();

   //Loads XML as a document
   public Optional<Document> readXML(Document xmlDoc){
       lock.lock();
       try {
           // modify your collections safely here
       } finally {
           lock.unlock();
       }
   }

   //reads the root node and triggers the recursive extraction of the XML data
   public List<Record> extractRootNodeAndTriggersRecursive(){
       lock.lock();
       try {
           // modify your collections safely here
       } finally {
           lock.unlock();
       }
   }

   //recursively read hierarchical data in the XML
   public SomeObject extractXMLRecursively(Element elt){
       lock.lock();
       try {
           // same here, etc.
       } finally {
           lock.unlock();
       }
   }

 

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

Комментарии:

1. Спасибо за ваш быстрый ответ, Нехаев. Но как мне применить блокировку к нескольким спискам / картам, вызываемым разными методами? Нужно ли мне блокировать и разблокировать все методы, которые имеют какой-либо общий ресурс, т.е. карту / список / набор?

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

3. Это имеет смысл, благодаря миллиону. Это означает, что когда spring создает экземпляр класса service, только один поток сможет использовать его одновременно. Верно? Как насчет вариантов, предложенных fps в моем исходном сообщении?

4. CopyOnWriteArrayList, ConcurrentHashMap и т. Д. Намного Лучше, Чем lock, Если у вас есть только одна коллекция в качестве состояния вашего сервиса. Если вам нужно сохранить согласованное состояние для нескольких коллекций (или любых других объектов), вы должны использовать lock .

5. Привет, Нехаев, я ввел логику, необходимую для тестирования и т. Д., Но я хотел бы узнать о шаблоне проектирования, используемом для решения проблемы параллелизма без использования блокировки, если таковая имеется. Мой коллега предположил, что я мог бы использовать параметр метода и возвращаемый тип, т.Е. Передать соответствующую коллекцию / карту в любой метод, который обновляет / заполняет ее, а затем возвращает обновленную коллекцию / карту. Я думал, что это будет беспорядочно, поскольку существует несколько карт и список, которые будут обновляться, когда я просматриваю большой XML-файл и проверяю элементы в его узлах. Есть идеи.