#spring #multithreading
#весна #многопоточность
Вопрос:
У меня есть вопрос относительно потокобезопасности. Например: у меня есть RestController с двумя конечными точками :
@RestController
class MyController {
@Autowired
MyService service;
@GetMapping
public void add(int i) {
service.add(i);
}
@GetMapping
public void delete(int i) {
service.delete(i);
}
}
@Component
class MyService {
private List<Integer> list;
public void add(int i) {
list.add(i);
}
public void delete(int i) {
list.remove(i);
}
}
Как я понимаю, поскольку MyService имеет одноэлементную область видимости, его список локальных переменных будет разделяться между потоками, а не потокобезопасностью? Если да, каковы наилучшие методы решения этой проблемы?
Комментарии:
1. Правильно, ваш код не является потокобезопасным. Однако ответ на вопрос, как решить эту проблему, является сложным. Это зависит. Вы можете попробовать использовать потокобезопасную структуру данных, например
CopyOnWriteArrayList
, или использовать правильную синхронизацию сsynchronized
блокировками и / или. Для получения дополнительной информации об этом вам нужно немного изучить многопоточность в Java. Книга Java Concurrency на практике — это первая книга по этой теме.2. Какова наилучшая практика? Например, у меня есть одна коллекция и один, скажем, класс Util. Коллекция, которую я могу выполнить потокобезопасно и определить объект Utils с помощью запроса области действия компонента?
3. Самая простая правильная вещь, которую вы можете сделать с вашим текущим кодом , — это объявить методы
add()
иdelete()
assynchronized
в вашем сервисе. Однако это не то, что вы бы на самом деле делали на практике по нескольким причинам, и фактическое решение зависит от требуемого параллелизма. В общем, эмпирическое правило заключается в том, что все обращения кlist
должны быть каким-то образом синхронизированы.4. Я думаю , что наиболее распространенным подходом является использование блокировки из
java.util.concurrent.locks
, скорееReentrantReadWriteLock
всего, (хотяStampedLock
работает лучше, если его можно использовать), или просто добавлениеsynchronized { ... }
блоков в вашиadd()
delete()
методы and . Однако большинство подходов каким-то образом ограничивают параллелизм, поэтому необходимо принять обоснованное решение. Однако для простых решений приведенное выше будет работать корректно.5. «решение состоит в том, чтобы определить компонент с запросом области видимости» … Это действительно зависит от логики вашего приложения. Обычно любые классы утилит вообще не создаются, поскольку они не должны содержать никакого состояния. Если у вас есть состояние, тогда это зависит. Если состояние привязано к одному запросу, конечно, ваше решение подходит. Если это не так, то нет. Добро пожаловать в архитектуру программного обеспечения 🙂