Implementation principle of volatile in Java high concurrency

Implementation principle of volatile in Java high concurrency

Absrtact: synchronized and volatile play an important role in multithreaded concurrent programming. Volatile is a lightweight synchronized, which ensures the "visibility" of shared variables in multiprocessor development. Visibility means that when one thread modifies a shared variable, another thread can read the modified value. It is less expensive than synchronized in some cases

1. Definition:

Java programming language allows threads to access shared variables. In order to ensure that shared variables can be updated accurately and consistently, threads should ensure that they can obtain this variable separately through exclusive locks. The Java language provides volatile, which is more convenient than locks in some cases. If a field is declared volatile, the java thread memory model ensures that all threads see the same value of this variable

2. Implementation principle of volatile

So how does volatile ensure visibility? Under the x86 processor, get the assembly instructions generated by the JIT compiler through the tool to see what the CPU will do when writing to the volatile.

Java code: instance = new singleton()// Instance is a volatile variable

Assembly code: 0x01a3de1d: movb $0x0,0x1104800 (% ESI); 0x01a3de24: lock addl $0x0,(%esp);

The second line of assembly code will be added when writing shared variables modified by volatile variables. It can be seen from the IA-32 architecture software developer manual that the instruction with lock prefix will cause two things in multi-core processors.

The data of the current processor cache row is written back to the system memory.

This write back operation will invalidate the data cached in the memory address in other CPUs.

In order to improve the processing speed, the processor does not directly communicate with the memory, Instead, read the data in the system memory to the internal cache first (L1, L2 or other) and then perform the operation. However, it is not known when it will be written to memory after the operation. If the volatile variable is declared, the JVM will send a lock prefix instruction to the processor to write the data of the cache line where the variable is located back to the system memory. However, even if it is written back to memory, if the cached value of other processors is still old, it will be executed again There will be problems in computing operations. Therefore, in order to ensure that the caches of each processor are consistent, the cache consistency protocol will be implemented. Each processor checks whether its cache value is expired by sniffing the data transmitted on the bus. When the processor finds that the memory address corresponding to its cache line has been modified, The cache line of the current processor will be set to an invalid state. When the processor wants to modify the data, it will force the data to be read from the system memory to the processor cache again.

The lock prefix instruction causes the processor cache to write back to memory. The lock prefix instruction causes the processor's lock# signal to be asserted during instruction execution. In a multiprocessor environment, a lock # signal ensures that the processor has exclusive access to any shared memory while the signal is asserted. (because it locks the bus, other CPUs cannot access the bus, and failure to access the bus means failure to access the system memory). However, in the recent processor, the lock signal generally does not lock the bus, but locks the cache. After all, the cost of locking the bus is relatively large. Chapter 8.1.4 describes in detail the impact of locking operation on the processor cache, for Intel 486 and Pentium processors , during lock operation, the lock# signal is always asserted on the bus. However, in P6 and the nearest processor, if the accessed memory area has been cached inside the processor, the lock# signal will not be asserted. On the contrary, it will lock the cache of this memory area and write back to memory, and use the cache consistency mechanism to ensure the atomicity of modification. This operation is called "cache locking", and the cache consistency mechanism will prevent modifying the memory area data cached by more than two processors at the same time.

Writing back the cache of one processor to memory will invalidate the cache of other processors. The IA-32 processor and Intel 64 processor use MESI (modified, exclusive, shared, invalid) control protocol to maintain the consistency between the internal cache and the cache of other processors. When operating in a multi-core processor system, IA-32 and Intel 64 processors can sniff other processors' access to system memory and their internal cache. They use sniffing technology to ensure its internal cache, system memory and the cache of other processors The data is consistent on the bus. For example, in Pentium and P6 family processors, if a processor is sniffed to detect that other processors intend to write a memory address, and this address is currently processing the shared state, the sniffing processor will invalidate its cache line, and the cache line filling will be enforced the next time the same memory address is accessed

Thank you for reading, hope to help you, thank you for your support to this site!

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>