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