JUC programming - the underlying implementation and analysis of synchronized

1 synchronized keyword

Synchronized is a keyword in Java, which is a kind of synchronization lock (also a pessimistic lock). The objects it modifies are as follows:

  • Acting on the instance method , the current instance is locked, and the lock of the current instance must be obtained before entering the synchronization code - the object lock ;
  • Act on the code block , lock the object configured in the brackets - object lock ;
  • Acting on static methods , the current class is locked, and the lock of the current class object must be obtained before entering the synchronization code—— class lock ;

Note: synchronized cannot modify variables;

2 javap instruction

javap is an anti-parsing tool that comes with jdk. Its function is to de-parse the code area (assembly instruction), local variable table, exception table, code line offset mapping table, constant pool and other information corresponding to the current class according to the class bytecode file.

javap -c
  • -c stands for disassembling the code;
  • javap -v ***.class file decompilation
  • -v -verbose output additional information, such as line number, local variable table, disassembly and other more detailed information;

3 Analyze synchronized implementation from the perspective of bytecode

3.1 Synchronized code blocks

code example

public class LockSyncTest {
    
    

    // 一个实例对象
    Object object = new Object();

    public void m1(){
    
    
        // 同步代码块
        synchronized (object) {
    
    
            System.out.println("这里是同步代码块");
        }
    }

    public static void main(String[] args) {
    
    

    }
}

decompile

javap -c LockSyncTest.class

generate content

Compiled from "LockSyncTest.java"
public class LockSyncTest {
    
    
  java.lang.Object object;

  public LockSyncTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: new           #2                  // class java/lang/Object
       8: dup
       9: invokespecial #1                  // Method java/lang/Object."<init>":()V
      12: putfield      #3                  // Field object:Ljava/lang/Object;
      15: return

  public void m1();
    Code:
       0: aload_0
       1: getfield      #3                  // Field object:Ljava/lang/Object;
       4: dup
       5: astore_1
       // 获得监视器并进入
       6: monitorenter
       7: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;        
      // here
      10: ldc           #5                  // String 这里是同步代码块
      12: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      15: aload_1
      // 锁退出
      16: monitorexit
      17: goto          25
      20: astore_2
      21: aload_1
      // 底层做了异常处理 如果程序出现异常 底层也要保证释放锁
      22: monitorexit
      23: aload_2
      24: athrow
      25: return
    Exception table:
       from    to  target type
           7    17    20   any
          20    23    20   any

  public static void main(java.lang.String[]);
    Code:
       0: return
}

two important instructions

  • monitorenter, get the lock;
  • monitorexit, release the lock (two, and release the lock when an exception occurs in the underlying implementation);

Must be one enter and two exits?
——Not necessarily, if you throw an exception in the synchronous code block, there is only one enter and one exit;

3.2 Common Synchronization Methods

code example

public class LockSyncTest {
    
    


    // 普通同步方法
    public synchronized void m2(){
    
    
        System.out.println("普通同步方法");
    }

    public static void main(String[] args) {
    
    

    }
}


decompile

javap -v LockSyncTest.class

generate content

The call instruction will check whether the ACC_SYNCHRONIZED access flag of the method is set. If it is set, the execution thread will first hold the monitor and then execute the method, and finally release the monitor when the method is completed (whether it is completed normally or abnormally);

Classfile out/production/ch16/LockSyncTest.class
  Last modified 2023-4-11; size 608 bytes
  MD5 checksum ff2328e7a59f847a473b7ed814cc108b
  Compiled from "LockSyncTest.java"
public class LockSyncTest
  SourceFile: "LockSyncTest.java"
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#21         //  java/lang/Object."<init>":()V
   #2 = Fieldref           #22.#23        //  java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #24            //  普通同步方法
   #4 = Methodref          #25.#26        //  java/io/PrintStream.println:(Ljava/lang/String;)V        
   #5 = Class              #27            //  LockSyncTest
   #6 = Class              #28            //  java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               LLockSyncTest;
  #14 = Utf8               m2
  #15 = Utf8               main
  #16 = Utf8               ([Ljava/lang/String;)V
  #17 = Utf8               args
  #18 = Utf8               [Ljava/lang/String;
  #19 = Utf8               SourceFile
  #20 = Utf8               LockSyncTest.java
  #21 = NameAndType        #7:#8          //  "<init>":()V
  #22 = Class              #29            //  java/lang/System
  #23 = NameAndType        #30:#31        //  out:Ljava/io/PrintStream;
  #24 = Utf8               普通同步方法
  #25 = Class              #32            //  java/io/PrintStream
  #26 = NameAndType        #33:#34        //  println:(Ljava/lang/String;)V
  #27 = Utf8               LockSyncTest
  #28 = Utf8               java/lang/Object
  #29 = Utf8               java/lang/System
  #30 = Utf8               out
  #31 = Utf8               Ljava/io/PrintStream;
  #32 = Utf8               java/io/PrintStream
  #33 = Utf8               println
  #34 = Utf8               (Ljava/lang/String;)V
{
    
    
  public LockSyncTest();
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       5     0  this   LLockSyncTest;

  public synchronized void m2();
    // ACC_SYNCHRONIZED标识
    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 普通同步方法
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 6: 0
        line 7: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       9     0  this   LLockSyncTest;

  public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 11: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       1     0  args   [Ljava/lang/String;
}

3.3 Static synchronization method

code example

public class LockSyncTest {
    
    


    // 静态同步方法
    public static synchronized void m3(){
    
    
        System.out.println("静态同步方法");
    }

    public static void main(String[] args) {
    
    

    }
}


decompile

javap -v LockSyncTest.class

generate content

ACC_STATIC, ACC_SYNCHRONIZED identifiers, to distinguish whether the method is a static synchronization method;

Classfile out/production/ch16/LockSyncTest.class
  Last modified 2023-4-11; size 590 bytes
  MD5 checksum e551241d2333dd7b754d24e72063d547
  Compiled from "LockSyncTest.java"
public class LockSyncTest
  SourceFile: "LockSyncTest.java"
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#21         //  java/lang/Object."<init>":()V
   #2 = Fieldref           #22.#23        //  java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #24            //  静态同步方法
   #4 = Methodref          #25.#26        //  java/io/PrintStream.println:(Ljava/lang/String;)V        
   #5 = Class              #27            //  LockSyncTest
   #6 = Class              #28            //  java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               LLockSyncTest;
  #14 = Utf8               m3
  #15 = Utf8               main
  #16 = Utf8               ([Ljava/lang/String;)V
  #17 = Utf8               args
  #18 = Utf8               [Ljava/lang/String;
  #19 = Utf8               SourceFile
  #20 = Utf8               LockSyncTest.java
  #21 = NameAndType        #7:#8          //  "<init>":()V
  #22 = Class              #29            //  java/lang/System
  #23 = NameAndType        #30:#31        //  out:Ljava/io/PrintStream;
  #24 = Utf8               静态同步方法
  #25 = Class              #32            //  java/io/PrintStream
  #26 = NameAndType        #33:#34        //  println:(Ljava/lang/String;)V
  #27 = Utf8               LockSyncTest
  #28 = Utf8               java/lang/Object
  #29 = Utf8               java/lang/System
  #30 = Utf8               out
  #31 = Utf8               Ljava/io/PrintStream;
  #32 = Utf8               java/io/PrintStream
  #33 = Utf8               println
  #34 = Utf8               (Ljava/lang/String;)V
{
    
    
  public LockSyncTest();
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       5     0  this   LLockSyncTest;

  public static synchronized void m3();
    // ACC_STATIC, ACC_SYNCHRONIZED标识符
    flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=0, args_size=0
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;      
         3: ldc           #3                  // String 静态同步方法
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 6: 0
        line 7: 8

  public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 11: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       1     0  args   [Ljava/lang/String;
}

4 What is the synchronized lock

Q: Why can any object be a lock?
A: In the HotSpot virtual machine, the monitor is implemented by ObjectMonitor;

4.1 What is a monitor?

Monitors, also called monitors , are a program structure in which multiple worker threads formed by multiple subprograms (objects or modules) within the structure mutually exclusive access to shared resources.

These shared resources are generally hardware devices or a group of variables. All operations that can be performed on shared variables are concentrated in one module. (The semaphore and its operating primitives are "encapsulated" inside an object) The monitor realizes that at one point in time, at most one thread is executing a certain subroutine of the monitor . The monitor provides a mechanism. The monitor can be regarded as a software module. It encapsulates the shared variables and the operations on these shared variables to form a functional module with a certain interface. The process can call the monitor to realize the process level . concurrency control .


4.2 Interpretation of the underlying C++ code

ObjectMonitor.java -> ObjectMonitor.cpp ->ObjectMonitor.hpp

Part of the source code of ObjectMonitor.hpp:

  ObjectMonitor() {
    
    
    _header       = NULL;
    _count        = 0;
    _waiters      = 0,
    _recursions   = 0;
    _object       = NULL;
    _owner        = NULL;
    _WaitSet      = NULL;
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;
    FreeNext      = NULL ;
    _EntryList    = NULL ;
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
    _previous_owner_tid = 0;
  }


Several key attributes in ObjectMonitor:

Guess you like

Origin blog.csdn.net/COINVK/article/details/130091844