#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-файл и проверяю элементы в его узлах. Есть идеи.