java多线程(1)- Synchronized的实现原理

反编译

/**
 * @author Hollis 17/11/9.
 */
public class SynchronizedTest {

    public synchronized void doSth(){
        System.out.println("Hello World");
    }

    public void doSth1(){
        synchronized (SynchronizedTest.class){
            System.out.println("Hello World");
        }
    }
}

javap的反编译结果:

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

可以看到对于synchronized方法,JVM使用ACC_SYNCHRONIZED标识符实现同步。对于synchronized代码库,JVM使用monitorenter、monitorexit两个指令来实现同步。

同步方法:

同步方法的常量池里会有一个ACC_SYNCHRONIZED标志,当某个线程要访问某个方法的时候,会检查是否有ACC_SYNCHRONIZED,如果有设置,则需要先获得监视器锁,然后开始执行。执行完后需要释放监视器锁。方法在执行的过程中,其他线程会因为无法获得该监视器锁而被阻断。

同步代码块:

可以把monitorenter理解为加锁,monitorexit理解为释放锁。每个对象维护着一个记录着被锁次数的计数器,未被锁定的对象的该计数器为0,当一个线程获得锁后,该计数器递增,当同一个线程再次获得该对象的锁,计数器再次自增。释放锁后递减。递减为0时其他线程可以获得锁。

comments powered by Disqus