I heard that wechat search “Java fish” will change strong oh!

This article is in Java Server, which contains my complete series of Java articles, can be read for study or interview

(1) Overview

In multithreaded program execution, it is possible that multiple threads will access a shared and variable resource at the same time. In this case, because the execution of the thread is uncontrollable, some methods must be adopted to control the access of the resource, which is “locking”.

Resources that can be accessed by multiple threads at the same time are called critical resources. The purpose of locking these critical resources is to make them accessible to only one thread at a time.

(2) Introduction to CAS

CAS: compare and swap. The CAS operation allows multiple threads to update a value without locking. The CAS operations are as follows:

When updating a value, obtain the current value E first and calculate the updated result value V (not updated at the moment). When updating the value, compare whether the value is still equal to E. If so, update E to V; if not, repeat the above operations.

I++ operation, for example, in the absence of lock, the operation is thread safe, suppose I of the initial value is 0, the CAS operation to get the original value of E = 0, calculating the value of the updated V = 1, to update before you compare if this value is equal to zero, if is equal to 0 will E update to 1, if is not equal to zero then have the thread has been updated, Obtain E =1 again and continue.

ABA problem

The CAS operation can have AN ABA problem, and the ABA problem is that the value E that we are comparing goes from 0 to 1 to 0 after multiple threads. At this point, the E value is the same as before, but it has been updated.

Solutions to the ABA problem

Add a version number to the value of E, and the version number will be obtained every time the data is obtained. After each data update, the version number will be increased, so that even if the value is equal, the version number can also know whether it has been modified.

Java uses CAS operations in a number of places, such as the Atomic classes:

AtomicInteger i=new AtomicInteger();
Copy the code

Looking inside the AtomicInteger method, you’ll see a class called Unsafe, in which you’ll see several operations on CAS

(3) object storage layout in memory

To learn synchronized, you must first understand the memory layout, or memory structure, of Java objects.

An object is divided into object headers, instance data, and its population.

The object Header takes 12 bytes, the Mark Word takes 8 bytes, and the type pointer Class Pointer takes 4 bytes (compressed by default, 8 bytes if compression is not enabled).

Instance objects have different sizes based on actual storage and are equal to 0 if the object is empty.

Padding denotes alignment. If the number of bytes in memory is not divisible by 8, fill in the corresponding number of bytes.

Take Object o=new Object() as an example. We import a joL dependency to see the specific memory layout

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.9</version>
</dependency>
Copy the code

Run the following code:

public static void main(String[] args) {
    Object o=new Object();
    System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
Copy the code

The first two lines are Mark Word (8 bytes). The third line is Class Pointer (4 bytes). At this point, the object is empty and the instance object is equal to 0.

(3) the synchronized

Synchronized ensures that only one thread can execute a method or block of code at a time. Synchronized stores lock information in MarkWord, the header of an object.

Synchronized locks a method on a non-static method, whereas synchronized locks the current class on a static method.

In earlier JDK versions, synchronized was a heavyweight lock that kept threads safe but was inefficient. Synchronized was later optimized to have a lock upgrade process:

Lock free (New) –> Bias lock –> Lightweight lock (spin lock) –> Heavyweight lock

Lock information is recorded through 8 bytes, or 64 bits, in MarkWord. Some people call spin locking lock free, because the optional operation does not lock an object, just understand the meaning here.

3.1 Details of lock upgrade Process:

When you add a synchronized lock to an object, you create a biased lock.

When a thread requests the object, the ID of the MarkWord object is changed to the current thread pointer ID (JavaThread), allowing only one thread to request the object.

The lock is upgraded to a lightweight lock when other threads also request it. Each thread generates a LockRecord in its own thread stack, and changes the request object MarkWordID to its own LockRecord with CAS spin operation. The successful thread requests the object, and the unsuccessful object continues to spin.

If the race heats up, when a thread spins more than a certain number of times (which is controlled by the JVM itself after JDK1.6), the lightweight lock is upgraded to a heavyweight lock, and the thread hangs, waiting for the operating system to dispatch it.

3.2 Bytecode implementation of locking

After the synchronized keyword is compiled into bytecode, it is translated into monitorenter and Monitorexit instructions. Monitorenter is executed when the synchronized code block is entered, and Monitorexit is executed after the synchronized code block is executed

(4) Lock elimination

In some cases, the LOCK will be automatically removed if the JVM decides it is not needed, as in this code:

public void add(String a,String b){
    StringBuffer sb=new StringBuffer();
    sb.append(a).append(b);
}
Copy the code

Stringbuffers are thread-safe, but stringbuffers are not shareable resources in this add method, so locking them only increases performance costs and the JVM removes the lock inside the StringBuffer.

(five) lock coarsening

In some cases, the JVM detects that a series of operations are repeatedly locking the same object and adds the lock to the string of operations, such as:

StringBuffer sb=new StringBuffer();
while(i<100){
    sb.append(str);
    i++;
}
Copy the code

The StringBuffer is locked and unlocked every time it adds data, 100 times in a row, at which point the JVM adds the lock to the outer while.

(6) Escape analysis

To start with a common virtual machine question, where do instance objects reside in the virtual machine? As in the previous answer, sample objects are placed on the heap, references are placed on the stack, and sample metadata and so on are stored in the method area or meta-space.

But this is only possible if the sample object has no thread escape behavior.

JDK1.7 starts with escape analysis enabled by default. Escape analysis means that if an object is found by the compiler to be accessible only by one thread, synchronization is not considered for that object. The JVM optimizes this type of object by converting heap allocation to stack allocation, which boils down to the virtual machine optimizing the program during compilation.

Enable escape analysis: XX:+DoEscapeAnalysis Closes escape analysis: XX: -doEscapeAnalysisCopy the code