Сначала о стиле кода.Viewer
s предоставляется необработанный доступ к массиву.Это противоречит философии ОО дизайна.Логический массив должен быть инкапсулирован ViewingStand
, обработанным только его методами.
После переписывания код будет выглядеть лучше, но все равно будет ошибочным из-за проблем с параллелизмом.
Доступ к фактическим данным, содержимому boolean[]
, не является энергозависимым.То, как вы используете ключевое слово, делает ссылку на данные непостоянной.Так как ссылка вообще не изменяется, добавленный volatile
ничего не делает, за исключением, может быть, замедления доступа.
Даже если вам удалось выполнить энергозависимые операции чтения и записи в содержимое массива, существуетпо-прежнему возникают проблемы с параллелизмом, поскольку проверка свободного места и его получение не являются атомарными.
Один из способов сделать доступ атомарным - добавить блокировки (сделать методы synchronized
), по сути, форсируя доступ к ViewingStand
случиться один за другим.С порядком «происходит до», навязанным блокировкой, вам даже не нужно volatile
.
Однако, если вы добавите блокировку к findSeat
, n + 1 th Viewer
будет удерживать замок, продолжайте искать свободное место, пока первые n Viewer
ждут блокировки, чтобы они могли запустить leaveSeat
.ТупикКлючевое слово synchronized
должно быть добавлено к методу, который циклически перебирает массив, но не к методу findSeat
.
Другой способ и мощная идея, которую можно применять даже в базе данных.Доступ Сравнение и замена .Это одна инструкция, которая изменяет данные, только если предыдущее значение соответствует ожидаемому.Вместо boolArray[i] = true
вы можете использовать массив AtomicBoolean
s и сделать atomicBoolArray[i].compareAndSet(false, true)
.Если compareAndSet
возвращает false, это означает, что другой Viewer
получил место ранее.