Synchronized is the keyword most commonly used in concurrent programming. Let’s explore synchronized:

  • How to use synchronized?
  • How does synchronized realize the synchronization lock, what is the principle?
  • What concurrent programming problems does Synchronized solve?

1 synchronized use

1.1 Thread safety Issues

In concurrent programming, thread-safety issues arise when multiple threads access the same resource at the same time.

Since the process executed by each thread is out of control, it is possible to cause the final result to be inconsistent with the actual expected result or directly cause the program to fail.

example

public class VolatileTest{
    public int inc = 0;
    public void increase(){
        inc++;
    }
    public static void main(String[] args){
        final VolatileTest test = new VolatileTest();
        for(int i = 0; i < 10; i++){
            new Thread(){
                public void run() {for(int j = 0; j < 1000 ; j++){
                        test.increase();
                    }
                }
            }.start();
        }
        while(Thread.activeCount()>1){ Thread.yield(); System.out.println(test.inc); }}}Copy the code

Test. Inc = 10000;

Result: The result of multiple executions is less than 10000;

Analysis: Multiple threads read the value of inc at the same time and increment the value of inc by 1.

In order to solve the thread safety problem, almost all concurrent modes adopt serialized access to critical resources. Simply speaking, only one thread can access the critical resources at a time, realizing synchronous mutually exclusive access. Generally speaking, is in the critical resource code before the operation of locking, must first obtain the lock, to access, after access to release the lock, other threads can access. In Java, there are two ways to implement synchronized mutually exclusive access: synchronized and LockCopy the code

1.2 use synchronized

  • For normal synchronization methods, the lock is the current instance object
  • Statically synchronized methods where the lock is the class object of the current class
  • Synchronized method block, lock is the object inside parentheses

example

public class MyClass{ int count; Public synchronized void add(int value){count += value; Public void add(int value){synchronized(this){count += value; synchronized(this){count += value; Public static static void add(intValue){count += value; } public static void add(int value){synchronized(myclass.class){count += value; }}}Copy the code

2. Principle exploration

The following code, using javap tools to view the generated class file information to analyze the implementation of Synchronized. Code:

Public class synchronized Test {// Synchronized Test public voiddoSth1(){
       synchronized (synchronizedTest.class){
           System.out.println("HelloWorld"); }} public synchronized voiddoSth2(){
        System.out.println("HelloWorld"); }}Copy the code

Javap class file decompiler results:

Javap commands:

D:\install\java\jdk8\bin\javap.exe -v .\synchronizedTest.class

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

Synchronized code block

The JVM uses monitorenter and Monitorexit to implement synchronization. The Java® Virtual Machine Specification[1] monitorenter and Monitorexit

The general content is as follows:

  • 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.

Synchronized methods

The JVM uses the ACC_synchronized tag for synchronization. Java® Virtual Machine Specification[2]

The general content is as follows

  • 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, 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.

3 Monitor

Both synchronized methods and synchronized code blocks are implemented based on the Monitor

What is the Monitor

All Java objects are born Monitor, and every Java object has the potential to be called Monitor because in Java design, every object is born with an invisible lock called an internal lock or Monitor lock.

Each object has a Monitor associated with it. The relationship between the object and its Monitor can be realized in various ways. For example, Monitor can be created and destroyed together with the object.

How does Moniter synchronize threads?

In the Java virtual machine (HotSpot), monitor is implemented by ObjectMonitor (located in the ObjectMonitor. HPP file of the HotSpot virtual machine source code, implemented in C++).

There are several key properties in ObjectMonitor:

_owner: points to the thread holding the ObjectMonitor object

_WaitSet: stores the queue of threads in wait state

_EntryList: stores the queue of threads in the lockwaiting block state

_recursions: number of lock reentrant times

_count: records the number of times the thread obtains the lock

  • Thread T waits for object lock: _EntryList to add T.

  • Thread T acquires the object lock: _EntryList removes T, _owner sets T, counter _count increments 1.

  • The lock object in thread T calls wait() : _owner is set to null, the counter _count is reduced by 1, and T is added to _WaitSet to wait to be awakened.

  • The thread T that holds the object lock completes: resets the value of the variable so that other threads can enter to acquire monitor

5 concludes

In multi-concurrent programming, thread safety is solved by mutually exclusive access to critical resources. In Java, synchronized blocks are commonly used to lock.

Synchronized is used in two ways, modifying methods and modifying blocks of synchronized code.

The implementation principle of synchronized: Each Java object is associated with a Monitor, and a synchronized object is locked through the operation of the Monitor on the thread.

Synchronized can guarantee atomicity, visibility and order in concurrent programming.

The resources

[1] The Java ® Virtual Machine Specification: docs.oracle.com/javase/spec…

[2] The Java ® Virtual Machine Specification: docs.oracle.com/javase/spec…


Reprinted from the Java Advanced Architect public account