/** * @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时其他线程可以获得锁。