Я вижу только две проблемы с вашим кодом:
одна проблема связана с упорядочением операций с памятью, упомянутым Стивеном С (можно решить, объявив next
и value
volatile
) (узел: значение имеет ту же проблему)
секунда более тонкая и не связана с параллелизмом: после возврата объекта в getObject
вы все равно сохраняете его ссылку из головы. Это может привести к утечке памяти.
В противном случае алгоритм в порядке. Неопределенная демонстрация (предположим, что выше исправлено):
L1: tail
никогда нельзя удалить из очереди. Это верно, потому что когда что-то хранится в tail
, оно имеет next == null
. Кроме того, когда вы присваиваете что-то xxx.next
(только в putObject
), оно не может быть tail
из-за атомарности getAndSet и порядка между volatile-записью и последующим чтением - предположим, что вы читаете ненулевое tail.next
это значение должно быть записано putObject
и, следовательно, происходит после последней строки. Это означает, что происходит после предыдущей строки, что означает, что значение, которое мы читаем, не из tail
.
Следствием этого является то, что каждый объект в putObject
будет в конечном итоге достижим с head
. Это потому, что мы подключаемся после tail
, и этот узел можно удалить только после того, как мы запишем ссылку на новый узел в его next
, что означает, что новый узел доступен с head
.
Добавленные объекты тривиально упорядочены с помощью операции getAndSet
в putObject
.
Снятые объекты упорядочены в соответствии с успешными compareAndSet
операциями в getObject
.
getObject
/ putObject
упорядочены в соответствии с записью / чтением в поле volatile next
.