Spring bean с сохранением состояния

Рейтинг: 1Ответов: 1Опубликовано: 29.03.2023

Моему приложению на базе Spring фреймворка при инициализации необходимо инициализировать несколько бинов значениями из базы данных. Данные инициализируются в методе аннотируемом @PostConstruct. Существует несколько бинов приведенного ниже вида. Хранение данных в базе данных продиктовано тем что изменения могут быть в рантайме без перезагрузки приложения. Таким образом бины с сохранением состояния должны быть потокобезопасными. Проблема: есть бины где полей очень много и придется расписывать каждый геттер и сеттер и в последующем при внесении каких либо изменений получается есть вероятность чего то забыть. Вопрос насколько правильно реализована потокобезопасность и есть ли более правильное решение

public class GlobalProperties {
    private String host;
    private TimeZone timeZone;
    private PropertiesService propertiesService;
    private final Lock writeLock;
    private final Lock readLock;
    @Autowired
    public GlobalProperties(PropertiesService propertiesService) {
        ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        this.readLock = lock.readLock();
        this.writeLock = lock.writeLock();
        this.propertiesService = propertiesService;
    }

    public GlobalProperties() {
        ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        this.readLock = lock.readLock();
        this.writeLock = lock.writeLock();
    }

    @PostConstruct
    public void init(){
       updateProperty((GlobalProperties) propertiesService.getProperty(GlobalProperties.class));
    }


    public void updateProperty(GlobalProperties newProperty){
        try {
            writeLock.lock();
            this.host = newProperty.getHost();
            this.timeZone = newProperty.getTimeZone();
        }finally {
            writeLock.unlock();
        }
    }

    public String getHost() {
        try {
            readLock.lock();
            return host;
        }finally {
            readLock.unlock();
        }
    }

    public TimeZone getTimeZone() {
        try {
            readLock.lock();
            return this.timeZone;
        }finally {
            readLock.unlock();
        }
    }```

Ответы

▲ 0Принят

Исключающая блокировка выглядит правильной, единственное, что я бы сохранял в поле ReentrantReadWriteLock, а read/writeLock брал непосредственно в методе (но это вкусовщина).

Окончательно убедиться в правильности реализации многопоточного кода можно с помощью JCStress. Вот этот пример вам подойдёт: https://github.com/openjdk/jcstress/blob/master/jcstress-samples/src/main/java/org/openjdk/jcstress/samples/primitives/mutex/Mutex_05_ReentrantLock.java

Также, если в вашем приложении host и timeZone соотносятся как ключ-значение, то вы можете реализовать эту же логику с помощью одного бина, содержащего ConcurrentHashMap<String, TimeZone>.