问题出现
代码验证
synchronized用法我们知道可以修饰实例方法,也可以修饰静态方法,还可以使用同步代码块。
public class Service {
//synchronized修饰实例方法
synchronized public void printA(){
System.out.println("I am printA");
}
//synchronized修饰静态方法
synchronized public static void printB(){
System.out.println("I am printB");
}
//synchronized同步代码块
public void printC(){
synchronized (this) {
System.out.println("I am printC");
}
}
}
反编译看
1.使用javac先编译
D:\vm>javac -encoding UTF-8 Service.java
2.使用javap查看编译后的代码
D:\vm>javap -verbose Service
修饰实例方法
public synchronized void printA();
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljav
a/io/PrintStream;
3: ldc #3 // String I am printA
5: invokevirtual #4 // Method java/io/PrintStream.prin
tln:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 7: 0
line 8: 8
修饰静态方法
public static synchronized void printB();
flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
Code:
stack=2, locals=0, args_size=0
0: getstatic #2 // Field java/lang/System.out:Ljav
a/io/PrintStream;
3: ldc #5 // String I am printB
5: invokevirtual #4 // Method java/io/PrintStream.prin
tln:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 12: 0
line 13: 8
同步代码块
public void printC();
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=1
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: getstatic #2 // Field java/lang/System.out:Ljav
a/io/PrintStream;
7: ldc #6 // String I am printC
9: invokevirtual #4 // Method java/io/PrintStream.prin
tln:(Ljava/lang/String;)V
12: aload_1
13: monitorexit
14: goto 22
17: astore_2
18: aload_1
19: monitorexit
20: aload_2
21: athrow
22: return
从上面可以看出来,synchronized修饰实例方法或静态方法都是通过标识ACC_SYNCHRONIZED实现同步。而同步代码块是是采用monitorenter、monitorexit两个指令来实现同步。
synchronized修饰方法
synchronized修饰方法,同步是隐式的。当某个线程要访问某个方法的时候,会检查是否有ACC_SYNCHRONIZED,如有,则需要先获取监视器锁,然后才开始执行方法。方法执行之后再释放监视器锁。在线程执行方法的时候,有另外线程也来请求执行该方法,会因为无法获取监视器锁而被阻断。
同步代码块
同步代码块是是采用monitorenter、monitorexit两个指令来实现同步。在执行monitorenter指令时,首先要尝试获取对象的锁。如果这个对象没被锁定,或者当前线程已经拥有了那个对象的锁,把锁的计数器加1,相应的,在执行monitorexit指令时会将锁计数器减1,当计数器为0的时候,锁就会被释放。如果获取对象锁失败,那当前线程就要阻塞等待。直到对象锁被另外一个线程释放为止。
monitorenter、monitorexit这两个字节码指令都需要一个reference类型的参数来明确要锁定和解锁的对象。上例中使用了synchronized (this)。