This article: Analysis of atomic class Atomicreference analysis source
In Atomicreference
Public Final Boolean Compareandset (v expect, v update) {return
unsafe.compareandswapobject (this, Valueoffset, expect, update);
}
Public final V Getandset (v newvalue) {return
(v) unsafe.getandsetobject (this, valueoffset, newvalue);
Public final Object Getandsetobject (object var1, Long var2, object VAR4) {
object var5;
do {
VAR5 = This.getobjectvolatile (var1, var2);} while (!this.compareandswapobject (var1, var2, Var5
, VAR4))
; return VAR5;
}
Analysis of Compareandswapobject Source code
Public Final Native Boolean Compareandswapobject (object var1, Long var2, Object Var4, Object VAR5);
You can see that the native method is declared, meaning that the method is implemented using a lower level of code (CPP) and needs to download the OPENJDK source to see it. The following is a brief introduction to the search source process.
After downloading good OPENJDK source code, import it into Idea's lib in this project, easy to find globally.
Refer to the native implementation rules to guess the CPP header files generated by the Javah tool.
Unsafe_entry (Jboolean, Unsafe_compareandswapobject (jnienv *env, Jobject UNSAFE, jobject obj, jlong offset, Jobject E_h, J Object x_h))
unsafewrapper ("Unsafe_compareandswapobject");
OOP x = Jnihandles::resolve (x_h); New value
oop e = Jnihandles::resolve (e_h);//Expected value
oop p = jnihandles::resolve (obj);
heapword* addr = (Heapword *) Index_oop_from_field_offset_long (P, offset);//In memory specific location
OOP res = Oopdesc::atomic_ Compare_exchange_oop (x, addr, E, true);//Another method was called
Jboolean success = (res = = e); If the returned res equals E, the compare condition is determined (stating that the res should be the current value in memory), but there is actually an ABA problem
if (success)//success is true, Indicates that the success has been exchanged at this time (called the lowest cmpxchg instruction)
Update_barrier_set ((void*) addr, x);///Every time reference type data writes, a write is generated Barrier temporarily interrupt operation, with garbage collector return
success;
Unsafe_end
Our direct global search (search scope is OPENJDK) This called method, you can see the definition and implementation of the method
Defined:
In the Opp.hpp file
Static OOP Atomic_compare_exchange_oop (OOP exchange_value,
volatile Heapword *dest,
oop compare_value,
BOOL Prebarrier = false);
Realize:
In the OPP.INLINE.HPP
(http://blog.csdn.net/lqp276/article/details/52231261 pointer compression related)
Inline OOP oopdesc::atomic_compare_exchange_oop (OOP exchange_value, Volati
Le Heapword *dest, oop compare_value, BOOL Prebarrier) {if (usecompressedoops) {//If compression common object pointer (compressedoops) is used, there is a process of renumbering if (Prebarrier)
{Update_barrier_set_pre ((narrowoop*) dest, exchange_value); }//encode Exchange and compare value from OOP to T narrowoop val = encode_heap_oop (Exchange_value); The new value Narrowoop cmp = Encode_heap_oop (compare_value); Expected value Narrowoop old = (Narrowoop) Atomic::cmpxchg (Val, (narrowoop*) dest, CMP);
The method invoked here is at the end of the article because it is compressed to 32 bits, so it can be operated with INT//decode old from T to OOP return Decode_heap_oop (old);
else {if (prebarrier) {update_barrier_set_pre (oop*) dest, exchange_value); Return (OOP) atomic::cmpxchg_ptr (Exchange_value, (oop*) dest, compare_value); You can see this continues to invoke the other methods}}
Actions for 64-bit addresses
Inline Jlong atomic::cmpxchg (jlong exchange_value, volatile jlong* dest , Jlong compare_value ) {
int MP = OS::IS_MP ();
Jint Ex_lo = (jint) exchange_value;
Jint Ex_hi = * ((jint*) &exchange_value) + 1);
Jint Cmp_lo = (jint) compare_value;
Jint Cmp_hi = * ((jint*) &compare_value) + 1);
__asm {
push ebx
push EDI
mov eax, cmp_lo
mov edx, cmp_hi
mov edi, dest
mov ebx, ex_lo
mov ecx, Ex_hi
lock_if_mp (MP)
cmpxchg8b qword ptr [edi]
pop edi
pop ebx
}
}
/**
cmpxchg8b-Compare and swap 8 bytes
Description
Compares 64-bit values and operands (target operands) in Edx:eax. If the two values are equal, the 64-bit value in the ECX:EBX is stored to the target operand. Otherwise, the value of the target operand is loaded into the edx:eax. The target operand is a 8-byte memory location. For a pair of edx:eax and ECX:EBX registers, EDX and ECX contain 64-bit values of 32 highs, EAX and EBX contain 32 lows.
This instruction can be used with the LOCK prefix, at which point the instruction is executed atomically. To simplify the processor's bus interface, the target operand can receive a write cycle regardless of the comparison result. If the comparison fails, the target operand is written back, otherwise the source operand is written to the target. (The processor never produces only lock reads but no lock writes).
* * Compareandswapint
To see a basic type of CAS source code
Unsafe_entry (Jboolean, Unsafe_compareandswapint (jnienv *env, Jobject UNSAFE, jobject obj, jlong offset, jint E, Jint x))
unsafewrapper ("Unsafe_compareandswapint");
OOP p = jnihandles::resolve (obj);
jint* addr = (Jint *) Index_oop_from_field_offset_long (P, offset);
Return (Jint) (Atomic::cmpxchg (x, addr, e)) = = e; This is where the CPU instructions above are called Cmpxchg
unsafe_end
In ATOMIC_WINDOWS_X86.INLINE.HPP:
MP represents multiprocessor, or multiprocessor. Finally, it transforms the assembly instruction according to the specific processor architecture to implement the CAs.
You need to precede the lock instruction when multiprocessor.
Inline Jint atomic::cmpxchg (jint exchange_value, volatile jint* dest , Jint compare_value ) {
//Alternative for interlockedcompareexchange
int MP = OS::IS_MP ();
__asm {
mov edx, dest //Eax,ecx,edx are 32-bit registers
mov ecx, exchange_value
mov eax, compare_value
LOCK_IF_MP (MP)
cmpxchg dword ptr [edx], ecx
}
}
The above is the CPU level of CAs, described below:
Compares the value in the "AL, AX, or EAX register (depending on the size of the" operand) with the "the" "The" "the" on operand). If the two values are equal, the second operand (source operand) is loaded into the destination operand. Otherwise, the destination operand is loaded into the AL, AX, or EAX register. (If the expectation equals the value of the object address store, replace the value of the object address store with the new value, or change the expectation to the current Like the value of the address store)
This instruction can is used with a LOCK prefix to allow instruction. To simplify the interface to the processor's bus, the destination operand receives a write cycle without regard to the Res Ult of the comparison. The destination operand is written back if the comparison fails; Otherwise, the source operand is written into the destination. (The processor never produces a locked read without also a producing write.)
CAS is the basis for the atomicity of all atomic variables, and why a seemingly unnatural operation is so important. The reason for this is that the native operation will eventually evolve into a CPU instruction CMPXCHG, rather than multiple CPU instructions. Since CAS is only a directive, it is not interrupted by multithreaded scheduling, so it is possible to ensure that CAS operations are atomic. To add, many contemporary CPU types support CMPXCHG operations, but not all CPUs are supported, and for unsupported CPUs, locks are automatically added to ensure that their operations are not interrupted.
As a result, atomic variables provide atomicity from CAS operations, CAs are from unsafe, and are then guaranteed by the CPU's CMPXCHG directives.
(The following should be for the byte data of CAs, but the specific operation is somewhat strange, mainly the assignment of that piece)
You can also look for functions in a different way without using global search.
First look at the files that appear in the #inclued file.
Find the following (because the previously called method is also atomic)
#include "runtime/atomic.inline.hpp"
Then enter the HPP file and find
#include "runtime/atomic.hpp"
This time we'll go directly to the Atomic.cpp file.
(What exactly is the jint that http://blog.csdn.net/lxf310/article/details/39719445→ appears)
jbyte atomic::cmpxchg (jbyte exchange_value, volatile jbyte* dest, Jbyte compare_value) {Asser
T (sizeof (jbyte) = = 1, "assumption.");
uintptr_t dest_addr = (uintptr_t) dest;
uintptr_t offset = dest_addr% sizeof (jint);
Volatile jint* Dest_int = (volatile jint*) (Dest_addr-offset); Jint cur = *dest_int; Current value jbyte* cur_as_bytes = (jbyte*) (&cur);
Cur's address jint new_val = cur; jbyte* new_val_as_bytes = (jbyte*) (&new_val); New_val's address//writes the new value first on the New_val address (because the last value is also taken from the jbyte* dest address).
) New_val_as_bytes[offset] = Exchange_value; The current value and expected values are compared, and the assumption is not satisfied, directly return Cur_as_bytes[offset], because unlike the Compare_value value, the upper method will determine the Exchange failure while (cur_as_bytes[offset] = =
Compare_value) {//attempt to exchange, invoke machine instruction, return the value of the current address jint res = CMPXCHG (new_val, dest_int, cur);
if (res = = cur) break;
Otherwise, modify cur cur = res;
New_val = cur;
New_val_as_bytes[offset] = Exchange_value;
return Cur_as_bytes[offset]; }