Deep parsing of Java Atomicinteger Atomic types
In concurrent programming we need to ensure that the program can get the correct results when it is accessed concurrently by multiple threads, that is, thread safety. Thread safety is defined as follows:
This class is thread-safe when multiple threads access a class, regardless of how the runtime environment is scheduled or how those threads will be executed alternately, and if no additional synchronization or collaboration is required in the keynote code, and the class behaves correctly.
Example of a thread that is unsafe. If we want to implement a function to count Web page accesses, you might want count++
to use it to count traffic, but this self-increment operation is not thread-safe. count++
can be divided into three operations:
- Gets the current value of the variable
- Gets the current variable value +1
- Write back the new value to the variable
Assuming that the initial value of Count is 10, when concurrent operations occur, both thread A and thread B may have performed 1 operations, followed by a 2 operation. A first to 3 operation +1, now the value is 11; Note that the current value that AB obtains is 10, so B performs a 3 operation, and the value of Count is still 11. The result is clearly not in line with our request.
So we need to use the protagonist--atomicinteger in this article to ensure thread safety.
The source code of Atomicinteger is as follows:
Package java.util.concurrent.atomic;import Sun.misc.Unsafe; Public classAtomicintegerextendsNumberImplementsJava.io.Serializable{Private Static Final LongSerialversionuid = 6214790243416807050L;//Setup to use Unsafe.compareandswapint for updates Private Static Finalunsafe unsafe = unsafe.Getunsafe();Private Static Final LongValueoffset;Static{Try{Valueoffset = unsafe.Objectfieldoffset(Atomicinteger.class.Getdeclaredfield("Value")); }Catch(Exception ex) {Throw NewError (ex); } }Private volatile intValue PublicAtomicinteger (intInitialValue) {value = InitialValue; } PublicAtomicinteger () {} Public Final int Get() {returnValue } Public Final void Set(intNewValue) {value = newvalue; } Public Final void Lazyset(intNewValue) {unsafe.Putorderedint( This, Valueoffset, NewValue); } Public Final int Getandset(intNewValue) { for(;;) {intCurrent =Get();if(Compareandset(current, NewValue))returnCurrent } } Public Final Boolean Compareandset(intExpectintUpdate) {returnUnsafe.Compareandswapint( This, Valueoffset, expect, update); } Public Final Boolean Weakcompareandset(intExpectintUpdate) {returnUnsafe.Compareandswapint( This, Valueoffset, expect, update); } Public Final int getandincrement() { for(;;) {intCurrent =Get();intNext = current +1;if(Compareandset(current, next))returnCurrent } } Public Final int getanddecrement() { for(;;) {intCurrent =Get();intNext = current-1;if(Compareandset(current, next))returnCurrent } } Public Final int Getandadd(intDelta) { for(;;) {intCurrent =Get();intNext = current + Delta;if(Compareandset(current, next))returnCurrent } } Public Final int Incrementandget() { for(;;) {intCurrent =Get();intNext = current +1;if(Compareandset(current, next))returnNext } } Public Final int Decrementandget() { for(;;) {intCurrent =Get();intNext = current-1;if(Compareandset(current, next))returnNext } } Public Final int Addandget(intDelta) { for(;;) {intCurrent =Get();intNext = current + Delta;if(Compareandset(current, next))returnNext } } PublicStringtoString() {returnInteger.toString(Get()); } Public int Intvalue() {return Get(); } Public Long Longvalue() {return(Long)Get(); } Public float Floatvalue() {return(float)Get(); } Public Double Doublevalue() {return(Double)Get(); }}
Let's look at the properties defined in the Atomic integer class
// setup to use Unsafe.compareAndSwapInt for updates privatestaticfinal Unsafe unsafe = Unsafe.getUnsafe(); privatestaticfinallong valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); catchthrownew Error(ex); } }
Unsafe is a tool class inside the JDK, which mainly implements platform-related operations. The following quote
Sun.misc.Unsafe is a tool class used internally by the JDK. It exposes Java-layer code by exposing some of the "unsafe" features in Java, allowing the JDK to use Java code more to implement features that are otherwise platform-related and that require the use of native languages such as C or C + +. This class should not be used outside the JDK core class library.
The specific implementation of unsafe is not related to the goal of this article, you just need to know that this code is to get the value in the heap memory offset is enough. Offsets are important in Atomicinteger, and atomic operations are achieved by it.
Definition and volatile of value
The Atomicinteger itself is an integral type, so the most important attribute is value, and let's see how it declares value.
privatevolatileint value;
We see that value uses a volatile
modifier, so what is it volatile
?
Volatile is equivalent to synchronized
a weak implementation, that is volatile
, the implementation of similar synchronized
semantics, but there is no lock mechanism. It ensures that volatile
updates to fields are communicated to other threads in a predictable manner.
volatile
Contains the following semantics:
- The Java storage model does not reorder the operations of the valatile instruction: This ensures that the operation of the volatile variable is performed in the order in which the instructions appear.
- Volatile variables are not cached in the register (only the owning thread is visible) or other places where the CPU is not visible, and the result of the volatile variable is always read from main memory each time. In other words, for changes to volatile variables, other threads are always visible and are not using variables inside their own thread stacks. That is, in the Happens-before rule, after a write operation to a valatile variable, any subsequent read operation understands the result of this write operation.
In short, the effect of volatile is that when a thread modifies a shared variable, another thread can read the modified value. In the analysis of Atomicinteger source code, we understand that this is enough.
Secure self-amplification with CAS operations
There are many methods in Atomicinteger, such as incrementAndGet()
equivalent i++
and getAndAdd()
equivalent i+=n
. From the source code we can see that the implementation of these methods is very similar, so we mainly analyze incrementAndGet()
the source code of the method.
The source code is as follows:
public Final int incrementandget () {for (; ;) {int current = get (); int next = current + 1 ; if (compareandset (current, next)) return next; }} public final boolean (int expect, int update) { unsafe. compareandswapint (this , Valueoffset, expect, update); }
incrementAndGet()
The method implements the self-increment operation. The core implementation is to get the current value and the target value (that is, value+1), and if compareAndSet(current, next)
the return succeeds, the method returns the target value. So what is compareandset to do? To understand this approach we need to introduce CAS operations.
In the university operating system course we learned the concept of exclusive and optimistic locking. An exclusive lock is when a thread acquires a lock and all other threads need to be suspended until the thread that holds the exclusive lock releases the lock, and the optimistic lock assumes that there is no conflict to proceed directly, and if it fails because of a conflict, retry until the operation succeeds. The mechanism in which optimistic locking is used is cas,compare and Swap.
The CAS operation in Atomicinteger is the compareAndSet()
function of extracting data from memory at each memory offset ( valueOffset
) , comparing the value taken with expect, and changing the value in memory to update if the data is consistent.
The use of CAs guarantees atomic manipulation. The principle of the remaining several methods is the same as this, which is no longer too much to explain.
Before looking at the Atomicinteger source code, I think its internal is used synchronized
to implement atomic operations. Data is found to synchronized
affect performance because locks in Java synchronized
are exclusive locks, although atomic operations can be implemented, but the concurrency of this implementation is poor.
Summarize
Summing up, the main implementation of Atomicinteger in the atomic operation of the integer, to prevent the occurrence of abnormal results in concurrency, its internal mainly relies on the unsafe class in the JDK operating memory data to achieve. The volatile modifier guarantees that value can be seen in memory by other threads that it is worth changing. CAS operations ensure that Atomicinteger can safely modify value values.
Deep parsing of Java Atomicinteger Atomic types