ReentrantLock分为公平锁和非公平锁,我们首先分析公平锁。 使用公平锁时,加锁方法lock()的方法调用轨迹如下: - ReentrantLock : lock()
- FairSync : lock()
- AbstractQueuedSynchronizer : acquire(int arg)
- ReentrantLock : tryAcquire(int acquires)
在第4步真正开始加锁,下面是该方法的源代码: - protected final boolean tryAcquire(int acquires) {
- final Thread current = Thread.currentThread();
- int c = getState(); //获取锁的开始,首先读volatile变量state
- if (c == 0) {
- if (isFirst(current) &&
- compareAndSetState(0, acquires)) {
- setExclusiveOwnerThread(current);
- return true;
- }
- }
- else if (current == getExclusiveOwnerThread()) {
- int nextc = c + acquires;
- if (nextc < 0)
- throw new Error("Maximum lock count exceeded");
- setState(nextc);
- return true;
- }
- return false;
- }
复制代码从上面源代码中我们可以看出,加锁方法首先读volatile变量state。 在使用公平锁时,解锁方法unlock()的方法调用轨迹如下: - ReentrantLock : unlock()
- AbstractQueuedSynchronizer : release(int arg)
- Sync : tryRelease(int releases)
在第3步真正开始释放锁,下面是该方法的源代码: - protected final boolean tryRelease(int releases) {
- int c = getState() - releases;
- if (Thread.currentThread() != getExclusiveOwnerThread())
- throw new IllegalMonitorStateException();
- boolean free = false;
- if (c == 0) {
- free = true;
- setExclusiveOwnerThread(null);
- }
- setState(c); //释放锁的最后,写volatile变量state
- return free;
- }
复制代码 从上面的源代码我们可以看出,在释放锁的最后写volatile变量state。公平锁在释放锁的最后写volatile变量state;在获取锁时首先读这个volatile变量。根据volatile的happens-before规则,释放锁的线程在写volatile变量之前可见的共享变量,在获取锁的线程读取同一个volatile变量后将立即变的对获取锁的线程可见。现在我们分析非公平锁的内存语义的实现。非公平锁的释放和公平锁完全一样,所以这里仅仅分析非公平锁的获取。使用公平锁时,加锁方法lock()的方法调用轨迹如下:- ReentrantLock : lock()
- NonfairSync : lock()
- AbstractQueuedSynchronizer : compareAndSetState(int expect, int update)
在第3步真正开始加锁,下面是该方法的源代码:- protected final boolean compareAndSetState(int expect, int update) {
- return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
- }
复制代码 该方法以原子操作的方式更新state变量,本文把java的compareAndSet()方法调用简称为CAS。JDK文档对该方法的说明如下:如果当前状态值等于预期值,则以原子方式将同步状态设置为给定的更新值。此操作具有 volatile 读和写的内存语义。这里我们分别从编译器和处理器的角度来分析,CAS如何同时具有volatile读和volatile写的内存语义。前文我们提到过,编译器不会对volatile读与volatile读后面的任意内存操作重排序;编译器不会对volatile写与volatile写前面的任意内存操作重排序。组合这两个条件,意味着为了同时实现volatile读和volatile写的内存语义,编译器不能对CAS与CAS前面和后面的任意内存操作重排序。下面我们来分析在常见的intel x86处理器中,CAS是如何同时具有volatile读和volatile写的内存语义的。
|