Send this article to the next person who asks you what the Java memory model is. The Java language provides a series of concurrency keywords, such as synchronized, volatile, final, and Concurren packages, to address the atomicity, visibility, and orderliness problems of concurrent programming.

In understanding the Java Virtual Machine, there is this passage:

The synchronized keyword can be used as one of the solutions for situations where atomicity, visibility, and orderliness are required, and seems to be “all-purpose.” Indeed, much of the concurrency control can be done using synchronized.

As Hemingway said in his book Death in the Afternoon, “The movement of an iceberg is magnificent because only one-eighth of it is above water.” To programmers, synchronized is just a keyword and is easy to use. The reason we don’t have to worry too much about multithreading is because this keyword hides a lot of the details.

Then, this paper focuses on synchronized, mainly introduces the usage of synchronized, the principle of synchronized, and how synchronized provides atomicity, visibility and order.

The use of the synchronized

Synchronized is a concurrency control keyword provided by Java. There are two main uses, synchronized methods and synchronized code blocks. That is, synchronized can modify both methods and blocks of code.

/** * @author Hollis 18/08/04. */ public SynchronizedDemo {public synchronized void doSth(){ System.out.println("Hello World"); Public void doSth1(){synchronizeddemo.class){system.out.println ("Hello World");} public void doSth1(){synchronizeddemo.class){system.out.println ("Hello World"); }}}Copy the code

Synchronized code blocks and methods can only be accessed by a single thread at a time.

The realization principle of synchronized

Synchronized is an important keyword used to solve the problem of concurrent data access in Java. When we want to ensure that a shared resource can only be accessed by one thread at a time, we can use the synchronized keyword to lock classes or objects in our code.

In the in-depth understanding of multi-threading (a) – synchronization implementation principle I have introduced its implementation principle, in order to ensure the integrity of knowledge, here is a brief introduction, detailed content please go to the original reading.

We decompiled 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: returnCopy the code

As you can see from the decompiled code, the JVM uses the ACC_SYNCHRONIZED tag for synchronization methods. For synchronous code blocks. The JVM uses monitorenter and Monitorexit to implement synchronization.

In The Java® Virtual Machine Specification, there is an introduction to The realization principle of synchronization method and synchronization code block.

Synchronization at the method level is implicit. Synchronized methods have an ACC_SYNCHRONIZED flag in their constant pool. When a thread accesses a method, it checks for ACC_SYNCHRONIZED, and if so, obtains the monitor lock, then executes the method, and then releases the monitor lock. If another thread requests to execute the method at this point, it will be blocked because it cannot acquire the monitor lock. It is important to note that 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.

The synchronization code block is implemented using monitorenter and Monitorexit directives. Monitorenter instructions can be understood as locks, and Monitorexit as locks. Each object maintains a counter that records how many times it has been locked. This counter is 0 for unlocked objects, increases to 1 when a thread acquires the lock (monitorenter), and increases again when the same thread acquires the lock again. When the same thread releases the lock (monitorexit directive), the counter decrement. When the counter is zero. The lock is released and other threads can acquire it.

Both ACC_SYNCHRONIZED, monitorenter and monitorexit are implemented based on Monitor. In Java virtual machine (HotSpot), Monitor is implemented based on C++ and implemented by ObjectMonitor.

ObjectMonitor provides several methods, such as Enter, Exit, wait, notify, and notifyAll. Sychronized The Enter method of the objectMonitor is called when the lock is added, and the exit method is called when the lock is unlocked. (Learn more about Moniter and learn more about multithreading.)

Synchronized and atomicity

Atomicity means that an operation is uninterruptible and must be performed completely or not at all.

What’s going on with our multithreading problems in concurrent programming in Java? Thread is the basic unit of CPU scheduling. The CPU has the concept of time slice and will schedule threads according to different scheduling algorithms. When a thread starts executing after the time slice is acquired, it loses CPU usage after the time slice is exhausted. So in multithreaded scenarios, atomicity problems occur because time slices rotate between threads.

In Java, two high-level bytecode instructions, Monitorenter and Monitorexit, are provided to ensure atomicity. As mentioned earlier, the Java keyword for these two bytecode instructions is synchronized.

Monitorenter and Monitorexit directives ensure that synchronized 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, synchronized can be used in Java to ensure that operations within methods and code blocks are atomic.

Thread 1 locks monitorenter when it executes the monitorenter command. No other thread can acquire the lock unless thread 1 proactively unlocks the Monitor. Even if thread 1 abandons the CPU during execution for some reason, such as running out of CPU time slices, it does not unlock it. Since synchronized locks are reentrant, the next time slice is still available only to him, and the code continues to execute. Until all the code is executed. That guarantees atomicity.

Synchronized and visibility

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

Send this article to the next person who asks you what the Java memory model is. Analysis: in the Java memory model provides all of the variables are stored in main memory, each thread has its own working memory, thread working memory to save the thread variable is used in main memory copy of a copy of the thread of variables all the operations must be done in working memory, and cannot be 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 is possible for thread 1 to change the value of a variable, but thread 2 is not visible.

As we saw earlier, synchronized modified code locks at the beginning of execution and unlocks after completion. To ensure visibility, a rule states that a variable must be synchronized back to main memory before it can be unlocked. After unlocking, subsequent threads can access the modified value.

Thus, the values of objects locked by the synchronized keyword are visible.

Synchronized and order

Orderliness means that the order in which a program is executed is the order in which the code is executed.

Send this article to the next person who asks you what the Java memory model is. In addition to the introduction of time slice, due to processor optimization and instruction rearrangement, CPU may also execute input code out of order, such as load->add->save may be optimized into Load ->save-> Add. This is where there may be an order problem.

It is important to note that synchronized does not prohibit instruction rearrangement and processor optimization. That said, synchronized can’t avoid the problems mentioned above.

So why does synchronized also offer a guarantee of order?

So this is going to take the idea of order a little bit further. The natural orderliness of Java programs can be summed up in the following sentence: all operations are naturally ordered if viewed within this thread. If you observe another thread in one thread, all operations are unordered.

This sentence is also the original sentence of “Understanding the Java Virtual Machine”, but how to understand? Zhou did not elaborate. I’m going to extend this a little bit, but it’s actually related to the as-if-serial semantics.

The as-if-serial semantics mean that the execution result of a single-threaded program cannot be changed no matter how much reordering is done (by the compiler and processor to improve parallelism). Compilers and processors, no matter how optimized, must adhere to the as-IF-Serial semantics.

The as-if-serial semantics guarantee that instruction reordering is limited in a single thread, and as long as the compiler and processor comply with the semantics, a single-threaded program can be considered to execute sequentially. Of course, there is actually a rearrangement, but we don’t have to worry about the interference of the rearrangement.

So, thanks to synchronized modified code, it can only be accessed by the same thread at a time. So it’s single-threaded. Therefore, order can be guaranteed.

Synchronized and lock optimization

The usage and principle of synchronized and its effect on concurrent programming have been introduced. Is a good keyword to use.

Synchronized is actually implemented by Monitor. The Enter method of objectMonitor is called when locking, and exit method is called when unlocking. In fact, only prior to JDK1.6 did synchronized implementations directly call ObjectMonitor’s Enter and exit, known as heavyweight locks.

In JDK1.6, therefore, the optimization of lock a lot, and lightweight locks, biased locking, lock elimination, adaptive spin locks, lock coarsening (spin lock in 1.4 there, is off by default, only JDK1.6 is on by default), these actions are in order to more efficient to share data between threads, solve the problem of competition.

About spin lock, lock coarcing and lock elimination, you can refer to the in-depth understanding of multi-threading (five) — Java VIRTUAL machine lock optimization technology, about lightweight lock and biased lock, has been in the scheduling, I will have a separate article later. Will be released exclusively on my blog (http://www.hollischuang.com) and the public (Hollis), stay tuned.

Well, with regards to the synchronized keyword, we have introduced its usage, principles, and how to ensure atomicity, sequentiality, and visibility, as well as extended information and thinking about lock optimization. We’ll talk more about volatile and its difference from synchronized. Stay tuned.