preface

Synchronized is a sharp edge to solve the problem of data synchronization access under concurrent conditions. So what is the underlying principle of synchronized? Now we’re going to peel off its heart, layer by layer, like an onion, and see what happens.

Github.com/whx123/Java…

Synchronized scenarios

The synchronized keyword can be used in methods or code blocks in the following ways, as shown in the figure:

Next, let’s peel back the first layer of synchronized and decompile the code blocks and methods it works with.

Synchronized acts on code blocks

public class SynchronizedTest {

    public void doSth(a){
        synchronized (SynchronizedTest.class){
            System.out.println("test Synchronized"); }}}Copy the code

Decompilation results in:

As you can see, synchronized is added to the code block, and monitorenter and Monitorexit are added. The JVM uses monitoRenter and Monitorexit to synchronize. How do MonitoRenter and Monitorexit synchronize? We’ll take off the second layer and continue our exploration.

Synchronized acts on methods

 public synchronized void doSth(){
            System.out.println("test Synchronized method" );
    }
Copy the code

Decompilation results in:

As can be seen from the figure, the method of synchronized keyword is added, and the ACC_SYNCHRONIZED tag is added. That is, the JVM implements synchronization by adding ACC_SYNCHRONIZED to the method access identifier (FLAGS).

Monitorenter, Monitorexit, ACC_SYNCHRONIZED

Monitorenter, Monitorexit, ACC_SYNCHRONIZED, monitoreXit, ACC_SYNCHRONIZED Let’s move on to the second layer:

monitorenter

Monitorenter describes the monitorenter command

Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:

If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.

If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.

If another thread already owns the monitor associated with objectref, the thread blocks until the monitor’s entry count is zero, then tries again to gain ownership.

Google Translate here:

Each object is associated with a Monitor. Monitor is locked if and only if it has an owner (owned). The thread executing the Monitorenter directive tries to get the corresponding monitor as follows:

Each object maintains a count of how many times it has been locked, which is 0 when the object is not locked. When the thread enters Monitor (executing the Monitorenter directive), it sets the counter to 1.

When the same thread acquires the lock on the object again, the counter increases again.

When another thread attempts to acquire the monitor, it blocks until the counter reaches zero.

Take a look at the following diagram for easy understanding:

monitorexit

Monitorexit directive introduction

The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref.

The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.

Google Translate here:

Only the owner thread of Monitor can execute the Monitorexit directive.

The thread decrement the monitor counter by one by executing the Monitorexit directive. If the counter is 0, the thread no longer owns Monitor. Other threads are allowed to attempt to acquire the Monitor.

Take a look at the following diagram for easy understanding:

ACC_SYNCHRONIZED

ACC_SYNCHRONIZED introduction

Method-level synchronization is performed implicitly, As part of method Invocation and return. A synchronized method is distinguished in the run-time constant pool’s method_info structure by the ACC_SYNCHRONIZED flag, which is checked by the method invocation instructions. When invoking a method for which ACC_SYNCHRONIZED is set, the executing thread enters a monitor, invokes the method itself, and exits the monitor whether the method invocation completes normally or abruptly. During the time the executing thread owns the monitor, no other thread may enter it. If an exception is thrown during invocation of the synchronized method and the synchronized method does not handle the exception, the monitor for the method is automatically exited before the exception is rethrown out of the synchronized method.

Google Translate here:

Synchronization at the method level is implicit as part of a method call. Synchronized methods have an ACC_SYNCHRONIZED flag in their constant pool.

When calling a method with the ACC_SYNCHRONIZED flag set, the thread of execution needs to acquire the monitor lock and then execute the method. After the method executes, the monitor lock is released. When the method returns normally or throws an exception, the corresponding Monitor lock is released.

During this time, if another thread requests to execute the method, it will be blocked because it cannot acquire the monitor lock.

If an exception occurs during method execution and is not handled internally, the monitor lock is automatically released before the exception is thrown outside the method.

Take a look at this flow chart:

Summary of Synchronized second level

  • Synchronized code blocks are implemented through Monitorenter and MonitoreXit. When a thread executes to monitorenter, it acquires a Monitor lock before it executes the following methods. The lock is released when the thread executes to Monitorexit.
  • When a thread executes a method with the ACC_SYNCHRONIZED flag, it obtains a monitor lock.
  • Each object maintains a lock counter, where 0 indicates that the lock can be acquired by other threads. Otherwise, only the thread currently locked can acquire the lock again.
  • Both synchronized methods and synchronized code blocks are synchronized underneath monitor.
  • Each object is associated with a Monitor that can be owned or released by a thread.

Ok, so there’s a little bit of confusion here, what is monitor, and why does it synchronize? How are objects associated with Monitor? Objective Don’t worry, we continue to peel off another layer, please look down.

Monitor the monitor

What exactly is a Montor? Let’s peel back the third layer of Synchronized. What is monitor? It can be understood as a synchronization tool, or synchronization mechanism, and is often described as an object. The operating system tube is the conceptual principle, ObjectMonitor is its principle realization.

The plumbing of the operating system

  • A tube (English: Monitors) is a program structure in which worker threads of subroutines (objects or modules) mutually exclusive access to shared resources.
  • These shared resources are typically hardware devices or a set of variables. A pipe program implements that at most one thread is executing a subroutine of a pipe program at any one time.
  • Compared with concurrent programs that modify data structures to achieve mutually exclusive access, pipe-side implementations greatly simplify programming.
  • A pipe provides a mechanism for a thread to temporarily abandon mutex access, wait for certain conditions to be met, and then regain execution rights to resume its mutex access.

ObjectMonitor

ObjectMonitor data structure

In the Java Virtual Machine (HotSpot), Monitor is implemented by ObjectMonitor and its main data structure is as follows:

 ObjectMonitor() { _header = NULL; _count = 0; _waiters = 0, _recursions = 0; _object = NULL; _owner = NULL; _WaitSet = NULL; / / inwaitState threads are added to _WaitSet _WaitSetLock = 0; _Responsible = NULL ; _succ = NULL ; _cxq = NULL ; FreeNext = NULL ; _EntryList = NULL ; // Threads in the lock block state are added to the list. _SpinClock = 0 ; OwnerIsThread = 0 ; }Copy the code

ObjectMonitor keyword

The meanings of several key fields in ObjectMonitor are shown as follows:

Working mechanism

Java Monitor works like this:

  • Threads that want to get monitor first enter the _EntryList queue.
  • When a thread gets the object’s monitor, it enters the _Owner field, sets it to the current thread, and increments the _count counter by 1.
  • If a thread calls wait(), it enters the _WaitSet queue. It releases the monitor lock, assigning _OWNER to NULL, decreasing _count by 1, and entering the _WaitSet queue to block and wait.
  • If another thread calls notify()/notifyAll(), it wakes up a thread in _WaitSet, which tries again to acquire the monitor lock and enters the _Owner area if it succeeds.
  • When the synchronization method completes, the thread exits the critical section, sets the owner of Monitor to NULL, and releases the monitor lock.

To make things more vivid, here’s an example:

Synchronized (this){// enter the _EntryList queuedoSth(); this.wait(); // enter the _WaitSet queue}Copy the code

What is monitor? How does an object relate to monitor? Ladies and gentlemen, let’s go ahead and peel off the next layer.

Object associated with monitor

How are objects associated with Monitor? Go straight to the picture:

We have a general understanding of how objects are associated with Monitor. Next, we will continue to discuss the object memory layout, object header and MarkWord layer by layer.

Object memory layout

In the HotSpot VIRTUAL machine, the layout of objects stored in memory can be divided into three areas: object headers, Instance Data, and object Padding.

  • Instance data: the effective information stored by the object and the attribute data information of the class, including the attribute information of the parent class;
  • Align padding: Because the virtual machine requires that the object’s starting address be a multiple of 8 bytes. Padding data does not have to exist, just for byte alignment.
  • Object header: Hotspot Vm object header contains two parts of data: Mark Word (Mark field) and Class Pointer (type Pointer).

Object head

The object header contains two parts of data: a Mark Word and a Class Pointer.

  • Class Pointer: a Pointer to an object’s Class metadata that the virtual machine uses to determine which Class instance the object is
  • Mark Word: Used to store the runtime data of the object itself. It is the key to implementing lightweight and biased locking.

Mark word

Mark Word is used to store the runtime data of the object itself, such as HashCode, GC generation age, lock status flags, locks held by threads, bias thread IDS, bias time stamps, etc.

In a 32-bit HotSpot virtual machine, if the object is not locked, the Mark Word 32bit space contains 25 bits to store the object hash code, 4 bits to store the age of the object, 2 bits to store the lock flag, and 1bit to be fixed at 0 for non-biased locking. Other states are shown in the figure below:

  • A pointer to a mutex is a pointer to a mutex.
  • Synchronized is a heavyweight lock, that is, synchronized object lock. The Mark Word lock identifier bit is 10, where the pointer points to the starting address of the Monitor object.
  • Suddenly, is not the feeling of a village! How are objects associated with monitor? Answer: Mark Word heavyweight lock, pointer to monitor address.

Synchronized peeled off the fourth layer of summary

How are objects associated with Monitor?

  • Object has object headers in it
  • The object header contains the Mark Word
  • The Mark Word pointer points to monitor

Lock the optimization

In fact, only prior to JDK1.6 did synchronized implementations directly call ObjectMonitor’s Enter and exit, known as heavyweight locks. A heavyweight lock, why use it so often? Starting with JDK6, the HotSpot VIRTUAL Machine development team optimized locks in Java by adding optimization strategies such as adaptive spin, lock elimination, lock coarser, lightweight and biased locking.

spinlocks

What is spinlock?

A spinlock is when a thread attempts to acquire a lock that is already occupied by another thread, instead of going into a thread suspension or sleep state, it is constantly checked to see if the lock has been released.

Why do you need spinlocks?

Threads blocking and waking up require the CPU to move from the user state to the core state, and frequent blocking and waking up are obviously hard on the CPU. Most of the time, locks only last for a short period of time, and it’s not worth it to block and wake up threads frequently. Hence the spin-lock.

Application scenario of spin lock

Spin-locks are suitable for situations where the critical region protected by the lock is small and the lock is held for a short time.

Spin lock some thoughts

Here, I want to talk about why ConcurrentHashMap abandons segmental locking in favor of CAS spin.

Lock elimination

What is lock elimination?

Lock shaving refers to the fact that the virtual machine just-in-time compiler runs on locks that require synchronization but detect that there is no possibility of competing for shared data.

Lock eliminates some thinking

I want to extend this to everyday code development, where some developers use locking even when there is no concurrency. If no concurrency is possible, use ConcurrentHashMap.

Lock coarsening

What is lock-and-lease?

The concept of lock slang is easy to understand, which is to connect multiple consecutive lock and unlock operations together to expand a wider range of locks.

Why lock-up?

When using a synchronized lock, you want to keep the scope of the synchronized block as small as possible — only synchronize in the actual scope of the shared data. The goal is to keep the number of operations that need to be synchronized as small as possible, so that if there is a lock contention, the thread waiting for the lock can acquire the lock as quickly as possible. However, if a series of continuous lock unlocking operations, may lead to unnecessary performance loss, so the concept of lock slang is introduced.

Lock-rent metaphor thinking

For example, buying a ticket to the zoo. When a teacher brings a group of young friends to visit, if the inspector knows that they are a group, he can treat them as a whole (lock-in) and check the tickets at one time, instead of having to check the tickets individually.

conclusion

Let’s just conclude with a Synchronized onion, if you want to peel my heart out layer by layer.

Reference and thanks

  • The tube side of Synchronized www.jianshu.com/p/32e136181…
  • Deep understanding of multithreading implementation principle of (a) – Synchronized www.hollischuang.com/archives/18…
  • Deep understanding of multithreading (5) – the Java virtual machine lock optimization technology www.hollischuang.com/archives/23…
  • In-depth Understanding of the Java Virtual Machine

Personal public account

Welcome to pay attention to, we study together, discuss together ha.