/ Java  

Java Multithreading 12: AtomicReference

AtomicReference introduction and function list

AtomicReference is to perform atomic operations on objects.

AtomicReference function list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Use null as initial value to create new AtomicReference。
AtomicReference()

// Use given initialValue to create new AtomicReference。
AtomicReference(V initialValue)

// If current value == expected, then atomically update the value
boolean compareAndSet(V expect, V update)

// Get current value
V get()

// Atomically get the new value and return old value
V getAndSet(V newValue)

// Set the new value
void lazySet(V newValue)

// Set the new value
void set(V newValue)

// Atomically sets the value to newValue if the current value == expectedValue
boolean weakCompareAndSet(V expect, V update)

Source code analyisi of AtomicReference

The source code of AtomicReference.java in JDK11.0.5 is as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
public class AtomicReference<V> implements java.io.Serializable {
private static final long serialVersionUID = -1848883965231344442L;
private static final VarHandle VALUE;

static {
try {
MethodHandles.Lookup l = MethodHandles.lookup();
VALUE = l.findVarHandle(AtomicReference.class, "value", Object.class);
} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}

private volatile V value;

/**
* Creates a new AtomicReference with the given initial value.
*
* @param initialValue the initial value
*/
public AtomicReference(V initialValue) {
value = initialValue;
}

/**
* Creates a new AtomicReference with null initial value.
*/
public AtomicReference() {
}

/**
* Returns the current value,
* with memory effects as specified by {@link VarHandle#getVolatile}.
*
* @return the current value
*/
public final V get() {
return value;
}

/**
* Sets the value to {@code newValue},
* with memory effects as specified by {@link VarHandle#setVolatile}.
*
* @param newValue the new value
*/
public final void set(V newValue) {
value = newValue;
}

/**
* Sets the value to {@code newValue},
* with memory effects as specified by {@link VarHandle#setRelease}.
*
* @param newValue the new value
* @since 1.6
*/
public final void lazySet(V newValue) {
VALUE.setRelease(this, newValue);
}

/**
* Atomically sets the value to {@code newValue}
* if the current value {@code == expectedValue},
* with memory effects as specified by {@link VarHandle#compareAndSet}.
*
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(V expectedValue, V newValue) {
return VALUE.compareAndSet(this, expectedValue, newValue);
}

/**
* Possibly atomically sets the value to {@code newValue}
* if the current value {@code == expectedValue},
* with memory effects as specified by {@link VarHandle#weakCompareAndSetPlain}.
*
* @deprecated This method has plain memory effects but the method
* name implies volatile memory effects (see methods such as
* {@link #compareAndExchange} and {@link #compareAndSet}). To avoid
* confusion over plain or volatile memory effects it is recommended that
* the method {@link #weakCompareAndSetPlain} be used instead.
*
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful
* @see #weakCompareAndSetPlain
*/
@Deprecated(since="9")
public final boolean weakCompareAndSet(V expectedValue, V newValue) {
return VALUE.weakCompareAndSetPlain(this, expectedValue, newValue);
}

// other methods
}

AtomicReference implements atomic operations through volatile and VarHandler.

  1. value is volatile type. This ensures that when a thread modifies the value of a value, the value seen by other threads is the latest value, that is, the modified volatile value.
  2. Set the value via VarHandler. This ensures that when a thread pool sets a value through a function (such as the compareAndSet function), its operation is atomic, that is, the thread will not be interrupted while operating on the value.

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class AtomicReferenceDemo {
public static void main(String[] args){
Person p1 = new Person(101);
Person p2 = new Person(102);

// Create atomicReference, initialize as p1
AtomicReference ar = new AtomicReference(p1);

// if ar's value is p1, then set it as p2
ar.compareAndSet(p1, p2);

Person p3 = (Person)ar.get();

System.out.println("p3 is " + p3);
System.out.println("p3.equals(p1) = " + p3.equals(p1));
}
}

class Person {
volatile long id;
public Person(long id) {
this.id = id;
}
public String toString() {
return "id:"+id;
}
}

Results:

1
2
p3 is id:102
p3.equals(p1) = false

When creating a new AtomicReference object ar, initialize it to p1.

Next, set it through the CAS function. If the value of ar is p1, then set it to p2.

Finally, get the object of ar and print the result. The result of p3.equals(p1) is false, because Person does not override the equals() method, but uses the equals() method inherited from Object.java. equals() in Object.java is actually called == To compare two objects, that is, whether the addresses of the two objects are equal.