1 Principle of No lock class
1.1 CAS
The process of the CAS algorithm is this: it contains 3 parameters CAs (v,e,n). V represents the variable to be updated, E represents the expected value, and n represents the new value. only if V
Value is equal to the E value, the value of V is set to N, if the V value is different from the E value, the current thread does not have an update.
Don't do it. Finally, CAS returns the true value of the current v. CAS operations are held in an optimistic manner, always thinking that they can successfully complete
Operation. When multiple threads use CAs to manipulate a variable at the same time, only one wins and succeeds, and the rest fails. Failed threads
Will not be suspended, only be told to fail, and allow to try again, and of course allow failed threads to abort the operation. Based on this principle, CAS
The operation does not have a lock immediately, and other threads can be found to interfere with the current thread and do the proper processing.
We will find that there are so many steps in CAs that it is not possible to switch the thread and change the value when you are about to assign a value when the V and e are the same. result in inconsistent data.
In fact, this worry is superfluous. The CAs whole operation process is an atomic operation, which is done by a CPU instruction.
1.2 CPU Instructions
The CPU instructions for CAs are CMPXCHG
The instruction code is as follows:
/*
accumulator = AL, AX, or EAX, depending on whether
a byte, word, or doubleword comparison is being performed
*/
if (accumulator = = destination) {
ZF = 1;
Destination = Source;
}
else {
ZF = 0;
accumulator = destination;
}
If the target value is equal to the value in the register, set a jump flag and set the original data to the target. If not, the jump flag is not set.
Java provides a lot of lock-free classes, the following is a description of the lock-free class.
2 The use of no class
We already know that no locks are much higher than blocking efficiency. Let's look at how Java implements these lock-free classes.
2.1. Atomicinteger
Atomicinteger, like integers, inherit from the number class
public class Atomicinteger extends number implements Java.io.Serializable
There are many CAS operations in the Atomicinteger, typically:
Public final Boolean compareandset (int expect, int update) {return
unsafe.compareandswapint (this, Valueoffset, expect, update);
}
To explain the Unsafe.compareandswapint method, he means that the value of this variable is set to update if the value of the variable whose offset on this class is Valueoffset is the same as the expected expect.
In fact, the variable with an offset of valueoffset is value.
static {
try {
valueoffset = Unsafe.objectfieldoffset
(AtomicInteger.class.getDeclaredField ("value"));
} catch (Exception ex) {throw new Error (ex);}
}
As we have said before, CAs may fail, but the cost of failure is very small, so the general implementation is in an infinite loop until it succeeds.
Public final int getandincrement () {for
(;;) {
int current = Get ();
int next = current + 1;
if (Compareandset (next)) return to current
;
}
2.2 Unsafe
From the class name, unsafe operations are unsafe operations, such as:
Set the value based on the offset (this feature has been seen in the Atomicinteger just described)
Park () (Stop this thread and mention it in a future blog)
The underlying CAS operation
Non-public APIs, in different versions of JDK, may vary significantly
2.3. atomicreference
Atomicinteger has been mentioned before, and of course there are atomicboolean,atomiclong and so on, all the same.
Here is to introduce the atomicreference.
Atomicreference is a template class
public class atomicreference<v> implements Java.io.Serializable
It can be used to encapsulate any type of data.
Like string.
Package test;
Import java.util.concurrent.atomic.AtomicReference; public class Test {public final static atomicreference<string> atomicstring = new Atomicreference<string>
;("Hosee"); public static void Main (string[] args) {for (int i = 0; i < i++) {final int num =
I New Thread () {public void run () {try {T
Hread.sleep (math.abs (int) math.random () *100));
catch (Exception e) {e.printstacktrace ();
} if (Atomicstring.compareandset ("Hosee", "ZTK")) {
System.out.println (Thread.CurrentThread (). GetId () + "Change value");
}else {System.out.println (Thread.CurrentThread (). GetId () + "Failed");
} };
}.start (); }
}
}
Results:
10Failed
13Failed
9Change value
11Failed
12Failed
15Failed
17Failed
14Failed
16Failed
18Failed
You can see that only one thread can modify the value, and the following threads can no longer be modified.
2.4.AtomicStampedReference
We'll find there's still a problem with CAS operations.
Like the previous Atomicinteger Incrementandget method.
Public final int Incrementandget () {for
(;;) {
int current = Get ();
int next = current + 1;
if (Compareandset (next)) return to
next;
}
Assuming that the current value=1 switches to another thread after the execution of a thread int = get (), the thread turns 1 to 2, and then another thread turns 2 again to 1. At this point again switch to the start of the thread, because the value is still equal to 1, so still can perform CAS operation, of course, addition is no problem, if some cases, the state of the data sensitive, such a process is not allowed.
The Atomicstampedreference class is needed at this point.
It internally implements a pair class to encapsulate values and timestamps.
private static class Pair<t> {
final T reference;
final int stamp;
Private Pair (T reference, int stamp) {
this.reference = reference;
This.stamp = stamp;
}
Static <T> pair<t> of (T reference, int stamp) {return
new pair<t> (reference, stamp);
}
The main idea of this class is to add a timestamp to identify each change.
The
//comparison setting parameters are: Expect to write new value expected timestamp new timestamp
public boolean compareandset (v expectedreference,
v newreference,
Int E Xpectedstamp,
int newstamp) {
Pair current = Pair;
Return
Expectedreference = current.reference &&
Expectedstamp = = Current.stamp &&
(n Ewreference = = Current.reference &&
Newstamp = = Current.stamp) | |
Caspair (Current, Pair.of (Newreference, Newstamp));
}
writes a new value and updates the new timestamp when the expected value is equal to the current time stamp.
Here's a atomicstampedreference scene, which may not be appropriate, but unexpected good scenes. The
scenario background is that a company can recharge a user with less balance, but only once per user.
Package test;
Import java.util.concurrent.atomic.AtomicStampedReference;
public class Test {static atomicstampedreference<integer> money = new Atomicstampedreference<integer> (
19, 0); public static void Main (string[] args) {for (int i = 0; i < 3; i++) {final int timest
AMP = Money.getstamp ();
New Thread () {public void run () {while (true) {while (true) {Integer m = money.g
Etreference ();
if (M <) {if Money.compareandset (m, M +, timestamp,
Timestamp + 1)) { SYSTEM.OUT.PRINTLN ("Recharge success, Balance:" + money.getrEference ());
Break
}} else {
Break
}
}
}
};
}.start (); New Thread () {public void run () {for (int i = 0; i < 100; i++) {while (true) {int timestamp = Mone
Y.getstamp ();
Integer m = money.getreference ();
if (M > Ten) {if (Money.compareandset) (M, m-10, timestamp, Timestamp + 1)) {System.out.prin TLN ("Consumption 10 yuan, Balance:" + MoneY.getreference ());
Break
}}else {break;
} try {thread.sleep (100); catch (Exception e) {//Todo:handle except
Ion}};
}.start (); }
}
Explain the code, there are 3 threads in the user recharge, when the user balance less than 20 o'clock, the user recharge 20 yuan. There are 100 threads in consumption, each consuming 10 yuan. User initially has 9 yuan, when using atomicstampedreference to implement, will only recharge the user once, because each operation makes time stamp +1. Run Result:
Recharge success, Balance:
10 yuan consumption, balance:
10 yuan consumption, balance:
consumption of 10 yuan, balance: 9
If implemented using Atomicreference or Atomic integer, it can result in multiple recharge.
Recharge success, Balance:
10 yuan consumption, balance:
10 yuan consumption, balance:
A successful recharge, balance: The consumption of
10 yuan, the balance: the
consumption of 10 yuan, balance:
A successful recharge, balance:
Consumption of 10 Yuan, balance: 29
2.5. Atomicintegerarray
The implementation of an array is but one more subscript than the Atomicinteger.
Public final Boolean compareandset (int i, int expect, int update) {return
Compareandsetraw (Checkedbyteoffset (i), ex pect, update);
}
Its interior just encapsulates an ordinary array
Private final int[] array;
What's interesting inside is the use of a binary number of the leading zero arithmetic group offset.
Shift = 31-integer.numberofleadingzeros (scale);
A leading zero means, for example, a 8-bit representation of a 12,00001100, then a leading zero is the number of 0 in front of 1, which is 4.
How to calculate the specific offset, here is no longer introduced.
2.6. Atomicintegerfieldupdater
The main function of the Atomicintegerfieldupdater class is to allow ordinary variables to also enjoy atomic manipulation.
For example, there is a variable is an int type, and many places have applied this variable, but in a scene, want to make the int to Atomicinteger, but if the direct change of type, it is necessary to change the application elsewhere. Atomicintegerfieldupdater is to solve such problems.
Package test;
Import Java.util.concurrent.atomic.AtomicInteger;
Import Java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
public class Test {public static class v{int id;
volatile int score;
public int Getscore () {return score;
The public void SetScore (int score) {This.score = score; } public final static atomicintegerfieldupdater<v> vv = Atomicintegerfieldupdater.newupdater (V.class, "SC
Ore ");
public static Atomicinteger Allscore = new Atomicinteger (0);
public static void Main (string[] args) throws Interruptedexception {Final v stu = new V ();
thread[] t = new thread[10000]; for (int i = 0; I < 10000 i++) {T[i] = new Thread () {@Override P
ublic void Run () {if (Math.random () >0.4) { Vv.incrementanDget (Stu);
Allscore.incrementandget ();
}
}
};
T[i].start ();
for (int i = 0; I < 10000 i++) {t[i].join ();
} System.out.println ("Score=" +stu.getscore ());
System.out.println ("allscore=" +allscore); }
}
The code above will score using Atomicintegerfieldupdater into Atomicinteger. ensures thread safety.
This uses Allscore to verify that if the score and Allscore values are the same, they are thread safe.
Small Description:
Updater can only modify variables within its visible range. Because updater uses reflection to get this variable. If the variable is not visible, an error occurs. For example, if a variable is declared private, it is not feasible.
To ensure that the variable is read correctly, it must be of type volatile. If this type is not stated in our original code, then simply make a statement and it will not cause any problems.
Because the CAS operation is assigned directly through the offset in the object instance, it does not support static fields (Unsafe.objectfieldoffset () does not support statics).