Многопоточность Spring

#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() as synchronized в вашем сервисе. Однако это не то, что вы бы на самом деле делали на практике по нескольким причинам, и фактическое решение зависит от требуемого параллелизма. В общем, эмпирическое правило заключается в том, что все обращения к list должны быть каким-то образом синхронизированы.

4. Я думаю , что наиболее распространенным подходом является использование блокировки из java.util.concurrent.locks , скорее ReentrantReadWriteLock всего, (хотя StampedLock работает лучше, если его можно использовать), или просто добавление synchronized { ... } блоков в ваши add() delete() методы and . Однако большинство подходов каким-то образом ограничивают параллелизм, поэтому необходимо принять обоснованное решение. Однако для простых решений приведенное выше будет работать корректно.

5. «решение состоит в том, чтобы определить компонент с запросом области видимости» … Это действительно зависит от логики вашего приложения. Обычно любые классы утилит вообще не создаются, поскольку они не должны содержать никакого состояния. Если у вас есть состояние, тогда это зависит. Если состояние привязано к одному запросу, конечно, ваше решение подходит. Если это не так, то нет. Добро пожаловать в архитектуру программного обеспечения 🙂