正文
可以看到方法表的访问标志位 (flags) 中多了个 ACC_SYNCHRONIZED,然后看字节码指令区域 (Code) ,和普通方法没任何差别, 猜测 Java 虚拟机通过检查方法表中是否存在标志位 ACC_SYNCHRONIZED 来决定是否需要获取锁,至于获取锁的原理后文会提到。
然后看第二个使用 synchronized 区块的方法(Lock.print2)字节码:
public
static
void
print2
()
;
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=
2
, locals=
2
, args_size=
0
0
: ldc #
5
// 将锁对象 Lock.class 入栈
2
: dup
// 复制一份,此时栈中有两个 Lock.class
3
: astore_0
// 出栈一个 Lock.class 对象保存到局部变量表 Slot 1 中
4
: monitorenter
// 以栈顶元素 Lock.class 作为锁,开始同步
5
: getstatic #
2
// 5-10 调用 System.out.println("synchronized");
8
: ldc #
6
10
: invokevirtual #
4
13
: aload_0
// 将局部变量表 Slot 1 中的数据入栈,即 Lock.class
14
: monitorexit
// 使用栈顶数据退出同步
15
: goto
23
// 方法结束,跳转到 23 返回
18
: astore_1
// 从这里开始是异常路径,将异常信息保存至局部变量表 Slot 2 中,查看异常表
19
: aload_0
// 将局部变量表 Slot 1 中的 Lock.class 入栈
20
: monitorexit
// 使用栈顶数据退出同步
21
: aload_1
// 将局部变量表 Slot 2 中的异常信息入栈
22
: athrow
// 把异常对象重新抛出给方法的调用者
23
:
return
// 方法正常返回
Exception table:
// 异常表
from to target type
5
15
18
any
// 5-15 出现任何(any)异常跳转到 18
18
21
18
any
// 18-21 出现任何(any)异常跳转到 18
synchronized 区块的字节码相比较 synchronized 方法复杂了许多。每一行字节码的含义我都作了详细注释,可以看到此时是通过字节码指令 monitorenter,monitorexit 来进入和退出同步的。特别值得注意的是,我们并没有写 try.catch 捕获异常,但是字节码指令中存在异常处理的代码,其实为了保证在方法异常完成时 monitorenter 和 monitorexit 指令依然可以正确配对执行,编译器会自动产生一个异常处理器,这个异常处理器声明可处理所有的异常,它的目的就是用来执行 monitorexit 指令。这个机制确保在 synchronized 区块中产生任何异常都可以正常退出同步,释放锁资源。
不管是检查标志位中的 ACC_SYNCHRONIZED,还是字节码指令 monitorenter,monitorexit,锁机制的实现最终肯定存在于 JVM 中,后面我们会再提到这点。
接下来继续看 ReentrantLock 的实现,鉴于篇幅有限,ReentrantLock 的原理不会讲的很详细,感兴趣的可以自行研究。ReentrantLock 是基于并发基础组件 AbstractQueuedSynchronizer 实现的,内部有一个 int 类型的 state 变量来控制同步状态,为 0 时表示无线程占用锁资源,等于 1 时表示则说明有线程占用,由于 ReentrantLock 是可重入锁,state 也可能大于 1 表示该线程有多次获取锁。AQS 内部还有一个由内部类 Node 构成的队列用来完成线程获取锁的排队。本文只是简单的介绍一下 lock 和 unLock 方法。
下面先看 ReentrantLock.lock 方法:
// ReentrantLock.java
public
void
lock
()
{
this
.sync.lock();
}
// ReentrantLock.NonfairSync.class
final
void
lock
()
{
// 使用 cas 设置 state,如果设置成功表示当前无其他线程竞争锁,优先获取锁资源
if
(
this
.compareAndSetState(
0
,
1
)) {
// 保存当前线程由于后续重入锁的判断
this
.setExclusiveOwnerThread(Thread.currentThread());
}
else
{
this
.acquire(
1
);
}
}
// AbstractQueuedSynchronizer.java
public
final
void
acquire
(
int
arg)
{
if
(!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
// 如果阻塞被中断,重新设置中断通知调用者
}
// 判断是否是重入
protected
final
boolean
tryAcquire