Условия гонки с ссылками на Java
Атомное целое число, long, логическое значение и т. Д. Используются для выполнения любых атомарных обновлений соответствующих типов, поскольку может возникнуть состояние гонки, когда мы выполняем любые манипуляции с ними, например, ++. Но каковы разные случаи со ссылками, где могут быть такие условия гонки?
С уважением,
Кешав
3 ответа
Ссылки AFAIK не подчиняются условию гонки, потому что JVM гарантирует, что обновление ссылки является атомарной операцией (в отличие, например, от обновления long
где нижний и верхний 4 байта обновляются в два отдельных этапа). Единственный критический случай, как отметил SLaks, это compareAndSet
который не является атомным по своей природе. Это очень редко используется с нативными ссылками, но это известная идиома AtomicReference
когда необходимо обновить сразу две (или более) логически зависимые переменные. Java Concurrency на практике, раздел 15.3.1 публикует пример для этого, используя AtomicReference
обновить две переменные (хранящиеся в простом классе) в одной атомарной операции.
Основная причина существования AtomicReference
- кроме согласованности интерфейсов - это наглядность и безопасность публикации. В этом смысле атомная переменная "лучше volatile
".
Операции как ++
подвержены условиям гонки, потому что они включают в себя несколько дискретных операций (выборка, приращение, сохранение).
Установка ссылки (a = b
) является отдельной операцией и, следовательно, не зависит от условий гонки.
Операции над ссылочными типами (a.someMethod()
) может делать все, что захочет, и может зависеть от условий гонки.
В целях обучения я написал ConcurrentLinkQueue, используя AtomicReference.
package concurrent.AtomicE;
import java.util.concurrent.atomic.AtomicReference;
public class ConcurrentLinkQueue<V> {
private final AtomicReference<Node> firstNodePointer = new AtomicReference<Node>();
public void fastOffer(final V data){
final Node<V> newNode = new Node<V>(data,Thread.currentThread().getName());
System.out.println(newNode);
AtomicReference<Node> pointer = firstNodePointer;
for(;;){
if(pointer.get() == null){
if(pointer.compareAndSet(null,newNode)){
return;
}
}
pointer = pointer.get().getNext();
}
}
private static class Node<V>{
private AtomicReference<Node> next = new AtomicReference<Node>();
private volatile V data = null;
private String threadName = "";
Node(V data1,String threadName){
this.data = data1;
this.threadName = threadName;
}
@Override
public String toString() {
return "threadName=" + threadName +
", data=" + data;
}
private AtomicReference<Node> getNext() {
return next;
}
private void setNext(AtomicReference<Node> next) {
this.next = next;
}
private V getData() {
return data;
}
private void setData(V data) {
this.data = data;
}
}