The usage of synchronized in java, the principle of synchronized, and how synchronized provides atomicity, visibility and order guarantee

In order to solve the problems of atomicity, visibility and order in concurrent programming, the Java language provides a series of keywords related to concurrent processing, such as , , , synchronizedand volatileso finalon concurren包.

In "In-depth Understanding of Java Virtual Machine", there is such a passage:

synchronizedKeywords can be used as one of the solutions when the three characteristics of atomicity, visibility and order are required, and it seems to be "universal". Indeed, most concurrency control operations can be done using synchronized.

Then, this article revolves around synchronizedit, mainly introducing synchronizedthe usage, synchronizedprinciples, and synchronizedhow to provide atomicity, visibility, and order guarantees.

usage of synchronized

synchronizedIs a concurrency control keyword provided by Java. There are two main uses, which are synchronized methods and synchronized code blocks. That is, synchronizedboth methods and code blocks can be decorated.

/**
 * @author Hollis 18/08/04.
 */
public class SynchronizedDemo {
     //同步方法
    public synchronized void doSth(){
        System.out.println("Hello World");
    }

    //同步代码块
    public void doSth1(){
        synchronized (SynchronizedDemo.class){
            System.out.println("Hello World");
        }
    }
}

Modified synchronizedcode blocks and methods can only be accessed by a single thread at a time.

The realization principle of synchronized

synchronized, is a very important keyword in Java to solve data synchronization access under concurrent conditions. synchronizedWhen we want to ensure that a shared resource is only accessed by one thread at a time, we can use keywords to lock classes or objects in the code .

In [In-depth understanding of multi-threading (1) - Synchronized implementation principle] [2], I have introduced its implementation principle. In order to ensure the integrity of knowledge, here is a brief introduction. For details, please read the original text.

We decompile the above code to get the following code:

public synchronized void doSth();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String Hello World
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return

  public void doSth1();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: ldc           #5                  // class com/hollis/SynchronizedTest
         2: dup
         3: astore_1
         4: monitorenter
         5: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         8: ldc           #3                  // String Hello World
        10: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        13: aload_1
        14: monitorexit
        15: goto          23
        18: astore_2
        19: aload_1
        20: monitorexit
        21: aload_2
        22: athrow
        23: return

It can be seen from the decompiled code that: for synchronization methods, JVM uses ACC_SYNCHRONIZEDmarkers to achieve synchronization. For synchronous code blocks. The JVM uses monitorentertwo monitorexitinstructions to achieve synchronization.

In [The Java® Virtual Machine Specification][3], there is an introduction to the implementation principles of synchronization methods and synchronization code blocks. I translate it into Chinese as follows:

Method-level synchronization is implicit. There will be a flag in the constant pool of the synchronized method ACC_SYNCHRONIZED. When a thread wants to access a method, it will check whether it is available ACC_SYNCHRONIZED. If it is set, it needs to obtain the monitor lock first, then start to execute the method, and release the monitor lock after the method is executed. At this time, if other threads request to execute the method, they will be blocked because they cannot obtain the monitor lock. It is worth noting that if an exception occurs during method execution and the exception is not handled inside the method, the monitor lock will be automatically released before the exception is thrown outside the method.

Synchronized code blocks are implemented using monitorenterand monitorexittwo directives. Executing monitorenterinstructions can be understood as locking, and execution monitorexitcan be understood as releasing locks. Each object maintains a counter that records the number of times it has been locked. The counter of the unlocked object is 0. When a thread acquires the lock (execution monitorenter), the counter increments to 1. When the same thread acquires the lock of the object again, the counter increments again. When the same thread releases the lock (executes monitorexitthe instruction), the counter is decremented again. when the counter is 0. The lock will be released and other threads can acquire the lock.

No matter it is ACC_SYNCHRONIZEDor monitorenter, monitorexitit is implemented based on Monitor. In the Java virtual machine (HotSpot), Monitor is implemented based on C++ and implemented by ObjectMonitor.

Several methods are provided in the ObjectMonitor class, such as enter, exit, wait, notify, notifyAlland so on. sychronizedWhen locking, the enter method of objectMonitor will be called, and the exit method will be called when unlocking. (For details about Monitor, see [In-depth understanding of multithreading (4) - the realization principle of Monitor][4])

synchronized and atomicity

Atomicity means that an operation is uninterruptible, and it must be executed completely, or it will not be executed at all.

What is going on with our multithreading problem in [Java's concurrent programming? ] [5] analyzed: thread is the basic unit of CPU scheduling. The CPU has the concept of time slices, and will perform thread scheduling according to different scheduling algorithms. When a thread starts executing after obtaining the time slice, after the time slice is exhausted, it will lose the right to use the CPU. Therefore, in a multi-threaded scenario, since time slices are rotated between threads, atomicity problems will occur.

In Java, in order to ensure atomicity, two high-level bytecode instructions monitorenterand are provided monitorexit. As mentioned earlier, these two bytecode instructions are the corresponding keywords in Java synchronized.

Through monitorenterthe and monitorexitinstruction, it can be guaranteed that synchronizedthe modified code can only be accessed by one thread at a time, and cannot be accessed by other threads until the lock is released. Therefore, it can be used in Java synchronizedto ensure that operations within methods and code blocks are atomic.

When thread 1 executes monitorenterthe instruction, it will lock the monitor. After locking, other threads cannot obtain the lock unless thread 1 actively unlocks it. Even during execution, for some reason, such as the CPU time slice is exhausted, thread 1 gives up the CPU, but he does not unlock it. And because synchronizedthe lock is reentrant, the next time slice can only be acquired by himself, and the code will continue to be executed. until all codes are executed. This guarantees atomicity.

synchronized and visibility

Visibility means that when multiple threads access the same variable, one thread modifies the value of the variable, and other threads can immediately see the modified value.

We are in [If someone asks you what the Java memory model is, send him this article. ][1] analyzed: The Java memory model stipulates that all variables are stored in the main memory, and each thread also has its own working memory. The working memory of the thread stores the main memory of the variables used in the thread. Memory copy copy, all operations on variables by threads must be performed in the working memory, and cannot directly read and write the main memory. Different threads cannot directly access variables in each other's working memory, and the transfer of variables between threads requires data synchronization between their own working memory and main memory. Therefore, it may happen that thread 1 changes the value of a variable, but thread 2 is not visible.

As we mentioned earlier, synchronizedthe modified code will be locked when it starts to execute, and will be unlocked after execution is completed. In order to ensure visibility, there is a rule like this: before unlocking a variable, the variable must be synchronized back to main memory. After unlocking in this way, subsequent threads can access the modified value.

Therefore, the value of the object locked by the synchronized keyword is visible.

synchronized and orderly

Sequence means that the order of program execution is executed in the order of code.

We are in [If someone asks you what the Java memory model is, send him this article. ][1] analyzed: In addition to the introduction of time slices, due to processor optimization and instruction rearrangement, the CPU may also execute the input code out of order, such as load->add->save may be optimized to load ->save->add. This is where there may be an orderly problem.

It should be noted here that synchronizedinstruction rearrangement and processor optimization cannot be prohibited. That is, synchronizedthe above-mentioned problems cannot be avoided.

So, why do you say that synchronizedorder guarantees are also provided?

This is to expand the concept of orderliness. The natural order in Java programs can be summed up in one sentence: If observed in this thread, all operations are naturally ordered. If one thread observes another, all operations are unordered.

The above sentence is also the original sentence in "In-depth Understanding of Java Virtual Machine", but how to understand it? Zhou Zhiming did not explain in detail. Here I will briefly expand, which is actually as-if-serial语义related to .

as-if-serialSemantics means: no matter how reordering (compiler and processor to improve parallelism), the execution result of a single-threaded program cannot be changed. No matter how compilers and processors optimize, as-if-serialthe semantics must be respected.

as-if-serial语义I won’t go into details here as-if-serial语义. Simply put, it is guaranteed that in a single thread, there are certain restrictions on instruction rearrangement, and as long as the compiler and processor comply with this semantics, then it can be considered that the single thread program is executed in order. of. Of course, there are actually rearrangements, but we don't need to care about the interference of such rearrangements.

So, due to synchronizedthe modified code, it can only be accessed by the same thread at the same time. Then it is single-threaded execution. Therefore, its order can be guaranteed.

synchronized and lock optimization

synchronizedThe usage, principle and effect on concurrent programming introduced earlier . is a great keyword to use.

synchronizedIn fact, it is implemented with the help of Monitor. The method of objectMonitor will be called when locking enter, and the method will be called when unlocking exit. In fact, only before JDK1.6, the implementation of synchronized will directly call the entersum of ObjectMonitor exit. This kind of lock is called a heavyweight lock.

Therefore, in JDK1.6, a lot of optimizations have been made on locks, and then there are lightweight locks, biased locks, lock elimination, adaptive spin locks, and lock coarsening (spin locks exist in 1.4, but the default is turned off, jdk1.6 is turned on by default), these operations are to share data more efficiently between threads and solve competition problems.

For spin locks, lock coarsening and lock elimination, please refer to [In-depth understanding of multithreading (5) - lock optimization technology for Java virtual machines][6]. Regarding lightweight locks and biased locks, they are already in scheduling planning , I will introduce a separate article later, and will exclusively publish it on my blog (http://www.hollishuang.com) and official account (Hollis), so stay tuned.

Well, regarding synchronizedkeywords, we introduced their usage, principles, and how to ensure atomicity, sequence, and visibility. At the same time, we also extended the information and thinking related to lock optimization.

Guess you like

Origin blog.csdn.net/zy_dreamer/article/details/132350503