This is the 7th day of my participation in the August Text Challenge.More challenges in August
preface
Atomicity, visibility, and order. The volatile keyword guarantees visibility and order, but does not guarantee atomicity. So how to solve the security problem of multi-threaded concurrency?
In fact, all concurrent patterns solve thread-safety problems by serializing access to critical resources. That is, only one thread can access a critical resource at a time, also known as synchronous mutex access.
In Java, there are two approaches, synchronized and Lock
Synchronized
Before JDK1.5, synchronized was a heavyweight Lock. Javs SE 1.6 optimized synchronized, and the concurrency performance of built-in Lock was basically the same as Lock.
1. Use Synchronized
(1) Common synchronization method, the lock is the current instance object this, the so-called method lock.
(2) static synchronization method, the lock object is the current Class object, namely (XXX. Class), the so-called Class lock
(3) Synchronized method block, lock is the object configured in Synchonized parentheses.
2. Underlying implementation principle of Synchronized
Let’s first look at how blocks of synchronized code work
public class SynchronizedDemo {
public void say(boolean isYou) {
synchronized (this) {
System.out.println("Hello"); }}}Copy the code
See the result of decompilation.
D:\me\jvm-learn\src\main\java>javac SynchronizedDemo.java
D:\me\jvm-learn\src\main\java>javap -c SynchronizedDemo > Demo.txt
2.1 monitorenter
Each object is a monitor lock. The monitor is locked when it is occupied, and the thread attempts to take ownership of the Monitor when it executes the Monitorenter instruction. Process:
- If the number of entries to Monitor is 0, the thread enters monitor, then sets the number of entries to 1, and the thread is the owner of Monitor.
- If the thread already owns the monitor and just re-enters, the number of entries into the monitor is increased by one.
- If the monitor is occupied by another thread, the thread blocks until the number of monitor entries is zero, and then tries again to acquire ownership of the monitor.
2.2 monitorexit
The thread executing monitorexit must be the owner of the monitor to which objectref corresponds. When the instruction is executed, the number of monitor entries decreases by 1. If the number of monitor entries decreases by 1, the thread exits the monitor and is no longer the owner of the monitor. Other threads blocked by the monitor can try to take ownership of the monitor.
If you look at the synchronous method, the static synchronous method is the same
public synchronized void method(a) {
System.out.println("Hello word...");
}
Copy the code
In this example, we disassemble the code using javap -c to see the compiled information, but we find that the -c command can not see the sychronized compiled instruction.
We can see that for normal methods, there is an additional ACC_SYNCHRONIZED identifier in the constant pool. This identifier is how the JVM synchronizes methods.
Each synchronization object has its own Monitor lock, as shown below:
We know that synchronized adds a lock to an object, but how does the object record the lock state? The answer is that the lock status is recorded in the Mark Word for each object
3. Memory layout of the object
As mentioned earlier in the introduction of the JVM, JVM objects are revisited here
In the JVM, objects are laid out in memory in three areas: object headers, instance data, and aligned padding.
3.1 object head
The HotSpot VIRTUAL machine object header contains two parts of information. The first part is the “Mark Word”, which stores the object’s own runtime data, such as HashCode, GC generation age, lock status flag, thread held lock, bias thread ID, bias timestamp, and so on.
The length of this data is 32 and 64 bits, respectively, on 32-bit and 64-bit VMS (regardless of the compression pointer enabled scenario), and is officially called the Mark Word.
The Java object header structure is described as follows:
The default Mark Word storage structure for 32-bit JVMS
During runtime, the data stored in Mark Word changes as the lock flag bit changes. Mark Word may change to store four types of data
Mark Word can store four types of data. On a 64-bit VM, Mark Word is 64-bit in size and its storage structure is as follows:
HotSpot VIRTUAL machine object header Mark Word
The last two bits of the object header store the flag bit of the lock. 01 is the initial state and is not locked. The object header stores the hash code of the object itself, and different contents are stored in the object header depending on the lock level.
The bias lock stores the ID of the thread currently occupying the object; Lightweight stores Pointers to lock records in the thread stack.
From here we can see that the “lock”, could be a lock head record + object reference pointer (whether a thread has lock the thread lock record address and object head pointer comparison), object may also be possible that the thread ID (to determine whether a thread has a lock head thread ID and object storage thread ID)
3.2 Mark Word in object header and Lock Record in thread
When a thread enters a block of synchronized code, if the synchronized object is not locked, i.e. its Lock flag bit is 01, the virtual machine first creates what we call a “Lock Record” in the stack of the current thread, which stores a copy of the Mark Word of the Lock object. Officials called the copy of the product the Taliban Mark Word.
3.3 Monitor
Any object has a Monitor associated with it, and when a Monitor is held, it is locked. Synchronized implementations in the JVM are based on entering and exiting Monitor objects to implement method synchronization and code block synchronization. Although the implementation details vary, they are implemented through pairs of MonitorEnter and MonitorExit directives.
Monitor Monitor has two synchronization modes: mutual exclusion and collaboration. In a multi-threaded environment, if data needs to be shared between threads, the problem of mutually exclusive access to data needs to be solved. The monitor can ensure that data on the monitor is accessed by only one thread at a time.
Monitor core components:
- Wait Set: this is where threads that blocked calling the Wait method are placed;
- Contention List: Contention queue where all threads requesting locks are placed first;
- Entry List: Threads in the Contention List that qualify as candidate resources are moved to the Entry List;
- OnDeck: At most one thread is competing for a lock resource at any given time. This thread is called OnDeck;
- ! Owner: specifies the current thread that releases the lock.
4. Lock optimizations
It’s a little bit too much and I’ll introduce it in the next chapter
The last
The underlying principles are basically conceptual things, many definitions are basically some original texts in the book, I just do a collation here, plus some of my own understanding, to facilitate the establishment of a structured knowledge system, here reference many documents, the list is not very complete, please forgive me if there is similar.
Reference documentation
“The Art of Concurrent Programming” — An in-depth analysis of the principle of Synchronization