Возможность состязания при использовании Java-замков - PullRequest
2 голосов
/ 04 июня 2019

Я написал класс Java, и кто-то просмотрел код и настоял, чтобы в методе calculate могло быть условие гонки. Вот упрощенная версия кода класса:

public class MyClass {
    private List<Integer> list;
    private final ReadWriteLock lock;

    public MyClass() {
        list = new ArrayList<>();
        lock = new ReentrantReadWriteLock();
    }

    public void add(Integer integer) {
        lock.writeLock().lock();
        try {
            list.add(integer);
        } finally {
            lock.writeLock().unlock();
        }
    }

    public void deleteAll() {
        lock.writeLock().lock();
        try {
            list.clear();
        } finally {
            lock.writeLock().unlock();
        }
    }

    public Integer calculate() {
        List<Integer> newList = new ArrayList<>();
        Integer result = 0;

        lock.readLock().lock();
        try {
            list.forEach(integer -> {
                // calculation logic that reads values from 'list' and adds only a subset of elements from 'list' in 'newList'
            });
        } finally {
            lock.readLock().unlock();
        }

        setList(newList);
        return result;
    }

    private void setList(List<Integer> newList) {
        lock.writeLock().lock();
        try {
            list = newList;
        } finally {
            lock.writeLock().unlock();
        }
    }
}

Теперь мой вопрос:

Может ли в этом методе действительно возникнуть состояние гонки, и если да, то как я могу его решить (используя блокировки или любой другой метод для обеспечения безопасности потока класса)?

Любой совет будет оценен.

Ответы [ 2 ]

4 голосов
/ 04 июня 2019

Существует промежуток времени между созданием newList и вызовом setList(newList).Мы можем предположить, что этот промежуток времени произвольно велик, и все может произойти, когда он длится, например, другой поток добавляет объект, который должен быть сохранен, но он будет потерян, когда вызов setList(newList) удалит list с этим новым объектом.

На самом деле, метод calculate модифицируется и должен выполнять всю работу под блокировкой записи.

1 голос
/ 04 июня 2019

Для пояснения вышесказанного ... утверждение

List<Integer> newList = new ArrayList<>();

... создает структуру данных (list ...), которая впоследствии будет использоваться в блоке кода, который предназначен для защиты lock.readLock().lock();, но не содержится в нем. Поэтому он не защищен.

Чтобы устранить проблему, объявление newList не должно включать инициализацию. Ничто, влияющее на предполагаемое значение этой переменной, не должно существовать вне защищенного блокировкой блока.

...