table of Contents
What is CAS
CAS means compare and swap, compare and exchange.
CAS is an atomic operation, and CAS uses an optimistic locking mechanism.
Many functions in JUC are built on CAS, and the bottom layer of various atomic classes uses CAS to achieve atomic operations. Used to solve the security problem of concurrency.
In addition, I have collected more than 20 years of company interview knowledge points, as well as various Java core knowledge points to share with you for free. If you want information, please click (click here) to get it for free !
Concurrency security issues
Give a typical examplei++
public class AddTest {
public volatile int i;
public void add() {
i++;
}
}
By javap -c AddTest
see bytecode instructions add method:
public void add();
Code:
0: aload_0
1: dup
2: getfield #2 // Field i:I
5: iconst_1
6: iadd
7: putfield #2 // Field i:I
10: return
i++
Is split into multiple instructions:
- Execute
getfield
to get the original memory value; - Execute
iadd
to add 1 operation; - Execute
putfield
write to write the accumulated value back to memory.
Assume a situation:
- When the
线程 1
execution arrivesiadd
, since it has not been executed yetputfield
, the value in the main memory area will not be refreshed at this time. - At this time, it
线程 2
enters and starts to run, just copy the value of the main memory area to the private memory area. 线程 1
Just executeputfield
and update the value of the main memory area, then线程 2
the copy at this time is the old one. The error appeared.
How to solve?
The simplest is to add synchronized to the add method.
public class AddTest {
public volatile int i;
public synchronized void add() {
i++;
}
}
Although simple and solved the problem, the performance is not good.
The best solution should be to use the CAS program that comes with JDK , as in the above example, use the AtomicInteger
class
public class AddIntTest {
public AtomicInteger i;
public void add() {
i.getAndIncrement();
}
}
Underlying principle
The principle of CAS is not complicated:
- Three parameters, a current memory value V, expected value A, updated value B
- If and only if the expected value A and the memory value V are the same, modify the memory value to B and return true
- Otherwise do nothing and return false
Take a AtomicInteger
class analysis, first look at the source code:
My environment here is Java11, if it is Java8, some internal names are slightly different.
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
/*
* This class intended to be implemented using VarHandles, but there
* are unresolved cyclic startup dependencies.
*/
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long VALUE = U.objectFieldOffset(AtomicInteger.class, "value");
private volatile int value;
//...
}
Unsafe
Class, this class is rarely useful for general development.
Unsafe
The bottom layer of the class is implemented in C/C++, so its methods are all modified by the native keyword.
It can provide hardware-level atomic operations, such as obtaining the location of an attribute in memory and modifying the field value of an object.
key point:
-
AtomicInteger
The value stored by the class is in thevalue
field, and thevalue
field isvolatile
-
In the static code block, and get the
Unsafe
instance, get thevalue
offset of the field in memoryVALUE
Next back to the previous example:
As above, the getAndIncrement()
bottom layer of the method uses CAS technology to ensure concurrency safety.
public final int getAndIncrement() {
return U.getAndAddInt(this, VALUE, 1);
}
getAndAddInt()
method:
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!weakCompareAndSetInt(o, offset, v, v + delta));
return v;
}
v
getIntVolatile(o, offset)
Get by method, its purpose is to get the value o
at the offset
offset, which o
is AtomicInteger
the value stored in the class, that is value
, the value of the offset
memory offset, ie VALUE
.
Focus , weakCompareAndSetInt
it is to realize the CAS core method
- If the
o
sum isv
equal, it proves that no other thread has changed this variable, and then thev
value is updated tov + delta
, wheredelta
is the updated incremental value. - On the contrary, CAS continues to operate in a spin mode, and this step is also an atomic operation.
analysis:
AtomicInteger
The original value is set to A,线程 1
and线程 2
each has a copy, the value is A.
线程 1
BygetIntVolatile(o, offset)
getting the value A, it线程 1
is suspended at this time .线程 2
ThegetIntVolatile(o, offset)
value A is also obtained through the method, and theweakCompareAndSetInt
method is executed to compare the memory value as A, and the memory value is successfully modified to B.- At this time , I compare the
线程 1
executionweakCompareAndSetInt
method and find that the value A in my hand is inconsistent with the value B of the memory, indicating that the value has been modified in advance by other threads. 线程 1
Re-executegetIntVolatile(o, offset)
to get the value value again, because the variable value is modified by volatile and has visibility, thread A continues to executeweakCompareAndSetInt
comparison and replacement until it succeeds
CAS needs attention
Use restrictions
CAS is an atomic operation supported by the CPU. Its atomicity is guaranteed at the hardware level. It cannot be used directly by ordinary users in Java, and can only be used with the aid atomic
of atomic classes under the package, with limited flexibility.
However, CAS can only guarantee the atomicity of a single variable operation. When multiple variables are involved, CAS can do nothing.
Atomicity does not necessarily guarantee thread safety. For example, in Java, volatile
cooperation is needed to ensure thread safety.
ABA problem
concept
CAS has a problem, an example is as follows:
线程 1
Remove A from memory location V- At this time, A is
线程 2
also taken from memory location V - At this time
线程 1
in the suspended state,线程 2
change the value of position V to B, and finally to A 线程 1
Re-execute at this time , and find that the value of position V has not changed, and continue to execute as expected.
Although at this time 线程 1
was successful, but it does not meet our real expectations, equal线程 2
狸猫换太子to 线程 1
playing.
This is the so-called ABA problem
solution
Introduce atomic references, atomic operations with version numbers.
Bring a version number to each of our operations, so that we can avoid ABA problems. Both the idea of optimistic lock.
-
Each time the value in the memory changes, the version number is updated.
-
When performing CAS operations, when comparing the values in the memory, the version numbers are also compared. Only when the two have not changed can the execution succeed.
-
AtomicStampedReference
Classes in Java use version numbers to solve ABA problems.
Cost issues under high competition
-
In a highly competitive environment with a high probability of concurrent conflicts, if the CAS fails all the time, it will always try again, and the CPU overhead is relatively high.
-
One way of thinking about this problem is to introduce an exit mechanism, such as failing to exit after the number of retries exceeds a certain threshold.
-
More importantly, avoid using optimistic locking in a highly competitive environment.
In addition, I have collected more than 20 years of company interview knowledge points, as well as various Java core knowledge points to share with you for free. If you want information, please click (click here) to get it for free !