前提
1.Lock 和 synchronzied 的可见性是怎么实现的?
lock: 利用volatile相关的Happens-before规则,java 的AbstractQueuedSynchronizer 内部持有一个volatile修饰的state 变量:
/**
* The synchronization state.
* 加锁会+1,解锁会-1;
*/
private volatile int state;
根据volatile 可见性规则, 多个线程在读取state 的值时,永远得到的都是最新的,也就是,当前线程,对变量的加锁和解锁,对下一个线程,都是可见的;
synchronzied synchronized 的解锁 Happens-Before 于后续对这个锁的加锁。 当然,也可以从字节码角度来看实现原理:
public class Demo_01 {
private final Object lock = new Object();
public void test() {
synchronized (lock) {
// do sth....
}
}
}
执行:javap -verbose Demo_01 截取部分字节码:
Code:
stack=2, locals=3, args_size=1
0: aload_0
1: getfield #3 // Field lock:Ljava/lang/Object;
4: dup
5: astore_1
6: monitorenter
7: aload_1
8: monitorexit
9: goto 17
12: astore_2
13: aload_1
14: monitorexit
15: aload_2
16: athrow
17: return
留意 第6行和第14行的 monitorenter 和 monitorexit,在这两个指令之间执行的代码,具备:顺序性,可见性,原子性;
一言概之:都是并发编程中 管程的实现;
下图,从程序员角度列出二者之间的差异:
Tip:
- 面试的时候,会问道: 关键字 synchronized 修饰实例方法、静态方法和代码块时,分别是什么含义?
- 答:
-
修饰实例方法时,给当前对象实例加锁;
-
修饰静态方法时,给当前类加锁;
-
修饰代码块时,给指定对象加锁。
-
进入同步代码/代码块之前需要获得当前对象/当前类/指定对象的锁。